Commit 7c945dec authored by Andrey Filippov's avatar Andrey Filippov
Browse files

command encoder for tiled memory read

parent 56006910
Loading
Loading
Loading
Loading
+359 −0
Original line number Diff line number Diff line
/*******************************************************************************
 * Module: cmd_encod_tiled_rd
 * Date:2015-01-23  
 * Author: andrey     
 * Description: Command sequencer generator for reading a tiled aread
 * up to 1 kB. Memory is mapped so 8 consecuitive rows have same RA, CA
 * and alternating BA (0 to 7). Data will be read in columns 16 bytes wide,
 * then proceding to the next column (if >1).
 * If number of rows is less than 8 it is possible to use keep_open_in input,
 * then there will be no ACTIVATE in other than first column and 
 * AUTO RECHARGE will be applied only to the last column (single column OK).
 * if number of rows >=8, that port is ignored. If number of rows is less than
 * 5 (less for slower clock) without keep_open_in tRTP may be not matched.
 *
 * Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
 * cmd_encod_tiled_rd.v is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  cmd_encod_tiled_rd.v is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *******************************************************************************/
`timescale 1ns/1ps

module  cmd_encod_tiled_rd #(
//    parameter BASEADDR = 0,
    parameter ADDRESS_NUMBER=       15,
    parameter COLADDR_NUMBER=       10,
//    parameter MIN_COL_INC=           3, // minimal number of zero column bits when incrementing row (after bank)  
    parameter CMD_PAUSE_BITS=       10,
    parameter CMD_DONE_BIT=         10 // VDT BUG: CMD_DONE_BIT is used in a function call parameter!
) (
    input                        rst,
    input                        clk,
// programming interface
//    input                  [7:0] cmd_ad,      // byte-serial command address/data (up to 6 bytes: AL-AH-D0-D1-D2-D3 
//    input                        cmd_stb,     // strobe (with first byte) for the command a/d
    input                  [2:0] start_bank,    // bank address
    input   [ADDRESS_NUMBER-1:0] start_row,     // memory row
    input   [COLADDR_NUMBER-4:0] start_col,     // start memory column in 8-bit bursts 
    input [ADDRESS_NUMBER+COLADDR_NUMBER-4:0] rowcol_inc_in, // increment {row.col} when bank rolls over, removed 3 LSBs (in 8-bursts)
    input                  [5:0] num_rows_in_m1,   // number of rows to read minus 1
    input                  [5:0] num_cols_in_m1,   // number of 16-pixel columns to read (rows first, then columns) - 1
    input                        keep_open_in,  // keep banks open (for <=8 banks only    
    
    input                        start,       // start generating commands
    output reg            [31:0] enc_cmd,     // encoded commnad
    output reg                   enc_wr,      // write encoded command
    output reg                   enc_done     // encoding finished
);
    localparam FULL_ADDR_NUMBER=ADDRESS_NUMBER+COLADDR_NUMBER; // excluding 3 CA lsb, but adding 3 bank
    localparam ROM_WIDTH=9;
    localparam ROM_DEPTH=4;
    
    localparam ENC_NOP=        0;
    localparam ENC_BUF_WR=     1;
    localparam ENC_DCI=        2;
    localparam ENC_SEL=        3;
    localparam ENC_CMD_SHIFT=  4; // [5:4] - command: 0 -= NOP, 1 - READ, 2 - PRECHARGE, 3 - ACTIVATE
    localparam ENC_PAUSE_SHIFT=6; // [7:6] - 2- bit pause (for NOP commandes)
    localparam ENC_PRE_DONE=   8;
    
    localparam ENC_CMD_NOP=      0; // 2-bit locally encoded commands
    localparam ENC_CMD_READ=     1;
//    localparam ENC_CMD_PRECHARGE=2;
    localparam ENC_CMD_ACTIVATE= 2; // using autoprecharge, so no PRECHARGE is needed. When en_act==0, ENC_CMD_ACTIVATE-> ENC_CMD_NOP (delay should be 0)
//    localparam REPEAT_ADDR=3;
    localparam LOOP_FIRST=   5; // address of the first word in a loop
    localparam LOOP_LAST=    6; // address of the last word in a loop
    localparam CMD_NOP=      0; // 3-bit normal memory RCW commands (positive logic)
    localparam CMD_READ=     3;
//    localparam CMD_PRECHARGE=5;
    localparam CMD_ACTIVATE= 4;
//    localparam AUTOPRECHARGE_BIT=COLADDR_NUMBER;
    
    reg   [ADDRESS_NUMBER-1:0] row;     // memory row
    reg   [COLADDR_NUMBER-4:0] col;     // start memory column in 8-bursts
    reg                  [2:0] bank;    // memory bank;
    reg                  [5:0] num_rows_m1;  // number of rows in a tile minus 1
    reg                  [5:0] num_cols128_m1;  // number of r16-byte columns in a tile  -1
    reg  [FULL_ADDR_NUMBER-4:0] rowcol_inc; // increment {row.col} when bank rolls over, remove 3 LSBs (in 8-bursts)
    
    reg                        keep_open;                        
    
    reg                        gen_run;
    reg                        gen_run_d;
    reg        [ROM_DEPTH-1:0] gen_addr; // will overrun as stop comes from ROM
    
    reg        [ROM_WIDTH-1:0] rom_r; 
    wire                       pre_done;
    wire                 [1:0] rom_cmd;
    wire                 [1:0] rom_skip;
    wire                 [2:0] full_cmd;
    reg                        done;
    
    reg [FULL_ADDR_NUMBER-4:0] top_rc; // top combined row,column,bank burst address (excludes 3 CA LSBs), valid/modified @pre_act
    reg                        first_col;
    reg                        last_col;
    wire                       pre_act; //1 cycle before optional ACTIVATE
    wire                       pre_read; //1 cycle before READ command
    reg                  [5:0] scan_row; // current row in a tile (valid @pre_act)
    reg                  [5:0] scan_col; // current 16-byte column in a tile (valid @pre_act)
    reg                        start_d; // start, delayed by 1 clocks
    wire                       last_row;
    reg [FULL_ADDR_NUMBER-1:0] row_col_bank;     // RA,CA, BA - valid @pre_act;
    reg [FULL_ADDR_NUMBER-1:0] row_col_bank_inc; // incremented RA,CA, BA - valid @pre_act_d;
    reg   [COLADDR_NUMBER-1:0] col_bank;// CA, BA - valid @ pre_read; 
    
    wire                       enable_act;
//    wire                       enable_autopre;
    reg                       enable_autopre;
    
    reg                        pre_act_d;
    reg                        other_row; // other than first row (valid/changed @pre_act)
    wire                 [2:0] next_bank_w;
    wire [ADDRESS_NUMBER+COLADDR_NUMBER-4:0] next_rowcol_w; // next row/col when bank rolls over (in 8-bursts)
    
    reg                        loop_continue;
    reg                        last_col_d; // delay by 1 pre_act cycles;
    

    assign     pre_done=rom_r[ENC_PRE_DONE] && gen_run;
    assign     rom_cmd=  rom_r[ENC_CMD_SHIFT+:2] & {enable_act,1'b0}; // disable bit 1 if activate is disabled (not the first column)
    assign     rom_skip= rom_r[ENC_PAUSE_SHIFT+:2];
    assign     full_cmd= rom_cmd[1]?CMD_ACTIVATE:(rom_cmd[0]?CMD_READ:CMD_NOP);
    
    assign last_row=       (scan_row==num_rows_m1);
    assign enable_act=     first_col || !keep_open; // TODO: do not forget to zero addresses too (or they will become pause/done)
    assign next_bank_w=    bank+1;
    assign next_rowcol_w=row_col_bank[FULL_ADDR_NUMBER-1:3]+rowcol_inc;
    
    assign pre_act=        rom_r[ENC_CMD_SHIFT+1]; //1 cycle before optional ACTIVATE
    assign pre_read=       rom_r[ENC_CMD_SHIFT]; //1 cycle before READ command
    
    always @ (posedge rst or posedge clk) begin
        if (rst)           gen_run <= 0;
        else if (start)    gen_run<= 1;
        else if (pre_done) gen_run<= 0;
        
        if (rst)           gen_run_d <= 0;
        else               gen_run_d <= gen_run;

        if (rst)        num_rows_m1 <= 0;                    
        else if (start) num_rows_m1 <= num_rows_in_m1;  // number of rows
        if (rst)        num_cols128_m1 <= 0;
        else if (start)         num_cols128_m1 <= num_cols_in_m1;  // number of r16-byte columns
        
        if (rst)         start_d <=0;
        else             start_d <=  start;
        
        if (rst)                      top_rc <= 0;
        else if (start_d)             top_rc <= {row,col};
        else if (pre_act && last_row) top_rc <= top_rc+1; // may increment RA  
        
        if (rst)                      pre_act_d <= 0;
        else if (start_d)             pre_act_d <= 0;
        else                          pre_act_d <= pre_act;
        
        if (rst)                      other_row <= 0;
        else if (pre_act)             other_row <= ~last_row;
        
        if (rst)                          row_col_bank <= 0;
        else if (start_d)                 row_col_bank <= {row,col,bank};
        else if (pre_act_d && ~other_row) row_col_bank <= {top_rc,bank};
        else if (pre_act_d)               row_col_bank <= row_col_bank_inc; 
        
        if (rst)    row_col_bank_inc<=0;
        else        row_col_bank_inc<=(&row_col_bank_inc[2:0]!=0)?
                                      {row_col_bank_inc[FULL_ADDR_NUMBER-1:3],next_bank_w}:
                                      {next_rowcol_w,row_col_bank_inc[2:0]};  

        if (rst)                      scan_row <= 0;
        else if (start_d)             scan_row <= 0;
        else if (pre_act)             scan_row <= last_row?0:scan_row+1;
        
        if (rst)                      scan_col <= 0;
        else if (start_d)             scan_col <= 0;
        else if (pre_act && last_row) scan_col <= scan_col+1; // for ACTIVATE, not for READ

        if (rst)                      first_col <= 0;
        else if (start_d)             first_col <= 1;
        else if (pre_act && last_row) first_col  <= 0;

        if (rst)                      last_col <= 0;
        else if (start_d)             last_col <= num_cols128_m1==0; // if single column - will start with 1'b1;
        else if (pre_act)             last_col <= (scan_col==num_cols128_m1); // too early for READ ?

        if (rst)                      last_col_d <= 0;
        else if (start_d)             last_col_d <= 0;
        else if (pre_act)             last_col_d <= last_col;

        if (rst)                      enable_autopre <= 0;
        else if (start_d)             enable_autopre <= 0;
        else if (pre_act)             enable_autopre <=  last_col_d || !keep_open; // delayed by 2 pre_act tacts form last_col, OK with a single column
        
        if (rst)                      col_bank<=0;
        else if (start_d)             col_bank<= {col,bank};
        else if (pre_read)            col_bank<= row_col_bank[COLADDR_NUMBER-1:0];
        
        if (rst)     loop_continue<=0;
        else loop_continue <=  (scan_col==num_cols128_m1) && last_row;                 
        
        if (rst)                     gen_addr <= 0;
        else if (!start && !gen_run) gen_addr <= 0;
        else if ((gen_addr==LOOP_LAST) && !loop_continue) gen_addr <= LOOP_FIRST; // skip loop alltogeter
        else                         gen_addr <= gen_addr+1; // not in a loop
    end
    
    always @ (posedge clk) if (start) begin
        row<=start_row;
        col <= start_col;
        bank <= start_bank;
        rowcol_inc <= rowcol_inc_in;
        keep_open <= keep_open_in && (|num_cols_in_m1[5:3]!=0);
    end
    
    // ROM-based (registered output) encoded sequence
    always @ (posedge rst or posedge clk) begin
        if (rst)           rom_r <= 0;
        else case (gen_addr)
            4'h0:  rom_r <= (ENC_CMD_ACTIVATE <<  ENC_CMD_SHIFT) | (1 << ENC_NOP); 
            4'h1:  rom_r <= (ENC_CMD_ACTIVATE <<  ENC_CMD_SHIFT); 
            4'h2:  rom_r <= (ENC_CMD_READ <<      ENC_CMD_SHIFT)                                              | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h3:  rom_r <= (ENC_CMD_ACTIVATE <<  ENC_CMD_SHIFT)                                              | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h4:  rom_r <= (ENC_CMD_READ <<      ENC_CMD_SHIFT)                          | (1 << ENC_BUF_WR) | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h5:  rom_r <= (ENC_CMD_ACTIVATE <<  ENC_CMD_SHIFT)                          | (1 << ENC_BUF_WR) | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h6:  rom_r <= (ENC_CMD_READ <<      ENC_CMD_SHIFT)                          | (1 << ENC_BUF_WR) | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h7:  rom_r <= (ENC_CMD_NOP <<       ENC_CMD_SHIFT)                          | (1 << ENC_BUF_WR) | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h8:  rom_r <= (ENC_CMD_READ <<      ENC_CMD_SHIFT)                          | (1 << ENC_BUF_WR) | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h9:  rom_r <= (ENC_CMD_NOP <<       ENC_CMD_SHIFT) | (2 << ENC_PAUSE_SHIFT) | (1 << ENC_BUF_WR) | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h10: rom_r <= (ENC_CMD_NOP <<       ENC_CMD_SHIFT)                                              | (1 << ENC_DCI) | (1 << ENC_SEL); 
            4'h11: rom_r <= (ENC_CMD_NOP <<       ENC_CMD_SHIFT) | (3 << ENC_PAUSE_SHIFT)                     | (1 << ENC_DCI);
            4'h12: rom_r <= (ENC_CMD_NOP <<       ENC_CMD_SHIFT) | (1 << ENC_PRE_DONE);
            default:rom_r <= 0;
       endcase
    end
    always @ (posedge rst or posedge clk) begin
        if (rst)           done <= 0;
        else               done <= pre_done;
        
        if (rst)           enc_wr <= 0;
        else               enc_wr <= gen_run || gen_run_d;
        
        if (rst)           enc_done <= 0;
        else               enc_done <= enc_wr || !gen_run_d;
        
        if (rst)             enc_cmd <= 0;
        else if (rom_cmd==0) enc_cmd <= func_encode_skip ( // encode pause
            {{CMD_PAUSE_BITS-2{1'b0}},rom_skip[1:0]}, // skip;   // number of extra cycles to skip (and keep all the other outputs)
            done,                                     // end of sequence 
            3'b0,                    // bank (here OK to be any)
            1'b0,                    //   odt_en;     // enable ODT
            1'b0,                    //   cke;        // disable CKE
            rom_r[ENC_SEL],          //   sel;        // first/second half-cycle, other will be nop (cke+odt applicable to both)
            1'b0,                    //   dq_en;      // enable (not tristate) DQ  lines (internal timing sequencer for 0->1 and 1->0)
            1'b0,                    //   dqs_en;     // enable (not tristate) DQS lines (internal timing sequencer for 0->1 and 1->0)
            1'b0,                    //   dqs_toggle; // enable toggle DQS according to the pattern
            rom_r[ENC_DCI],          //   dci;        // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0)
            rom_r[ENC_BUF_WR],       //   buf_wr;     // connect to external buffer (but only if not paused)
            1'b0);                   //   buf_rd;     // connect to external buffer (but only if not paused)
       else  enc_cmd <= func_encode_cmd ( // encode non-NOP command
            rom_cmd[1]? // activate
            row_col_bank[FULL_ADDR_NUMBER-1:COLADDR_NUMBER]: // top combined row,column,bank burst address (excludes 3 CA LSBs), valid/modified @pre_act
                    {{ADDRESS_NUMBER-COLADDR_NUMBER-1{1'b0}},
                        enable_autopre,
                        col_bank[COLADDR_NUMBER-1:3],
                        3'b0}, //  [14:0] addr;       // 15-bit row/column adderss
            rom_cmd[1]?
                row_col_bank[2:0]:
                col_bank[2:0],        // bank (here OK to be any)
            full_cmd[2:0],           //   rcw;        // RAS/CAS/WE, positive logic
            1'b0,                    //   odt_en;     // enable ODT
            1'b0,                    //   cke;        // disable CKE
            rom_r[ENC_SEL],          //   sel;        // first/second half-cycle, other will be nop (cke+odt applicable to both)
            1'b0,                    //   dq_en;      // enable (not tristate) DQ  lines (internal timing sequencer for 0->1 and 1->0)
            1'b0,                    //   dqs_en;     // enable (not tristate) DQS lines (internal timing sequencer for 0->1 and 1->0)
            1'b0,                    //   dqs_toggle; // enable toggle DQS according to the pattern
            rom_r[ENC_DCI],          //   dci;        // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0)
            rom_r[ENC_BUF_WR],       //   buf_wr;     // connect to external buffer (but only if not paused)
            1'b0,                    //   buf_rd;     // connect to external buffer (but only if not paused)     
            rom_r[ENC_NOP]);         //   nop;        // add NOP after the current command, keep other data
    end    
    

// move to include?

    function [31:0] func_encode_skip;
        input [CMD_PAUSE_BITS-1:0] skip;       // number of extra cycles to skip (and keep all the other outputs)
        input                      done;       // end of sequence 
        input [2:0]                bank;       // bank (here OK to be any)
        input                      odt_en;     // enable ODT
        input                      cke;        // disable CKE
        input                      sel;        // first/second half-cycle, other will be nop (cke+odt applicable to both)
        input                      dq_en;      // enable (not tristate) DQ  lines (internal timing sequencer for 0->1 and 1->0)
        input                      dqs_en;     // enable (not tristate) DQS lines (internal timing sequencer for 0->1 and 1->0)
        input                      dqs_toggle; // enable toggle DQS according to the pattern
        input                      dci;        // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0)
        input                      buf_wr;     // connect to external buffer (but only if not paused)
        input                      buf_rd;     // connect to external buffer (but only if not paused)
        begin
            func_encode_skip= func_encode_cmd (
                {{14-CMD_DONE_BIT{1'b0}}, done, skip[CMD_PAUSE_BITS-1:0]},       // 15-bit row/column adderss
                bank[2:0],  // bank (here OK to be any)
                3'b0,       // RAS/CAS/WE, positive logic
                odt_en,     // enable ODT
                cke,        // disable CKE
                sel,        // first/second half-cycle, other will be nop (cke+odt applicable to both)
                dq_en,      // enable (not tristate) DQ  lines (internal timing sequencer for 0->1 and 1->0)
                dqs_en,     // enable (not tristate) DQS lines (internal timing sequencer for 0->1 and 1->0)
                dqs_toggle, // enable toggle DQS according to the pattern
                dci,        // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0)
                buf_wr,     // connect to external buffer (but only if not paused)
                buf_rd,     // connect to external buffer (but only if not paused)
                1'b0);
        end
    endfunction

    function [31:0] func_encode_cmd;
        input               [14:0] addr;       // 15-bit row/column adderss
        input                [2:0] bank;       // bank (here OK to be any)
        input                [2:0] rcw;        // RAS/CAS/WE, positive logic
        input                      odt_en;     // enable ODT
        input                      cke;        // disable CKE
        input                      sel;        // first/second half-cycle, other will be nop (cke+odt applicable to both)
        input                      dq_en;      // enable (not tristate) DQ  lines (internal timing sequencer for 0->1 and 1->0)
        input                      dqs_en;     // enable (not tristate) DQS lines (internal timing sequencer for 0->1 and 1->0)
        input                      dqs_toggle; // enable toggle DQS according to the pattern
        input                      dci;        // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0)
        input                      buf_wr;     // connect to external buffer (but only if not paused)
        input                      buf_rd;     // connect to external buffer (but only if not paused)
        input                      nop;        // add NOP after the current command, keep other data
        begin
            func_encode_cmd={
            addr[14:0], // 15-bit row/column adderss
            bank [2:0], // bank
            rcw[2:0],   // RAS/CAS/WE
            odt_en,     // enable ODT
            cke,        // may be optimized (removed from here)?
            sel,        // first/second half-cycle, other will be nop (cke+odt applicable to both)
            dq_en,      // enable (not tristate) DQ  lines (internal timing sequencer for 0->1 and 1->0)
            dqs_en,     // enable (not tristate) DQS  lines (internal timing sequencer for 0->1 and 1->0)
            dqs_toggle, // enable toggle DQS according to the pattern
            dci,        // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0)
            buf_wr,     // phy_buf_wr,   // connect to external buffer (but only if not paused)
            buf_rd,     // phy_buf_rd,    // connect to external buffer (but only if not paused)
            nop,        // add NOP after the current command, keep other data
            1'b0        // Reserved for future use
           };
        end
    endfunction

endmodule