mcntrl_linear_rw.v 34 KB
Newer Older
1 2 3
/*******************************************************************************
 * Module: mcntrl_linear_rw
 * Date:2015-01-29  
4
 * Author: Andrey Filippov     
5 6 7
 * Description: Organize paged R/W from DDR3 memory in scan-line order
 * with window support
 *
8
 * Copyright (c) 2015 Elphel, Inc.
9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * mcntrl_linear_rw.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.
 *
 *  mcntrl_linear_rw.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
23
// TODO: ADD MCNTRL_SCANLINE_FRAME_PAGE_RESET to caller
24 25 26 27 28
module  mcntrl_linear_rw #(
    parameter ADDRESS_NUMBER=                   15,
    parameter COLADDR_NUMBER=                   10,
    parameter NUM_XFER_BITS=                     6,    // number of bits to specify transfer length
    parameter FRAME_WIDTH_BITS=                 13,    // Maximal frame width - 8-word (16 bytes) bursts 
29
    parameter FRAME_HEIGHT_BITS=                16,    // Maximal frame height
30
    parameter LAST_FRAME_BITS=                  16,     // number of bits in frame counter (before rolls over)
31
    parameter MCNTRL_SCANLINE_ADDR=            'h120,
32
    parameter MCNTRL_SCANLINE_MASK=            'h7f0, // both channels 0 and 1
33
    parameter MCNTRL_SCANLINE_MODE=            'h0,   // set mode register: {repet,single,rst_frame,na[2:0],extra_pages[1:0],write_mode,enable,!reset}
34 35
    parameter MCNTRL_SCANLINE_STATUS_CNTRL=    'h1,   // control status reporting
    parameter MCNTRL_SCANLINE_STARTADDR=       'h2,   // 22-bit frame start address (3 CA LSBs==0. BA==0)
36 37 38 39 40 41
    parameter MCNTRL_SCANLINE_FRAME_SIZE=      'h3,   // 22-bit frame start address increment (3 CA LSBs==0. BA==0)
    parameter MCNTRL_SCANLINE_FRAME_LAST=      'h4,   // 16-bit last frame number in the buffer
    parameter MCNTRL_SCANLINE_FRAME_FULL_WIDTH='h5,   // Padded line length (8-row increment), in 8-bursts (16 bytes)
    parameter MCNTRL_SCANLINE_WINDOW_WH=       'h6,   // low word - 13-bit window width (0->'h4000), high word - 16-bit frame height (0->'h10000)
    parameter MCNTRL_SCANLINE_WINDOW_X0Y0=     'h7,   // low word - 13-bit window left, high word - 16-bit window top
    parameter MCNTRL_SCANLINE_WINDOW_STARTXY=  'h8,   // low word - 13-bit start X (relative to window), high word - 16-bit start y
42 43
                                                      // Start XY can be used when read command to start from the middle
                                                      // TODO: Add number of blocks to R/W? (blocks can be different) - total length?
44
                                                      // Read back current address (for debugging)?
45
    parameter MCNTRL_SCANLINE_STATUS_REG_ADDR= 'h4,
46
    parameter MCNTRL_SCANLINE_PENDING_CNTR_BITS=2,     // Number of bits to count pending trasfers, currently 2 is enough, but may increase
47 48 49
                                                      // if memory controller will allow programming several sequences in advance to
                                                      // spread long-programming (tiled) over fast-programming (linear) requests.
                                                      // But that should not be too big to maintain 2-level priorities
50 51 52 53 54 55 56 57 58
    parameter MCNTRL_SCANLINE_FRAME_PAGE_RESET =1'b0, // reset internal page number to zero at the frame start (false - only when hard/soft reset)
    // bits in mode control word
    parameter MCONTR_LINTILE_NRESET =           0, // reset if 0
    parameter MCONTR_LINTILE_EN =               1, // enable requests 
    parameter MCONTR_LINTILE_WRITE =            2, // write to memory mode
    parameter MCONTR_LINTILE_EXTRAPG =          3, // extra pages (over 1) needed by the client simultaneously
    parameter MCONTR_LINTILE_EXTRAPG_BITS =     2, // number of bits to use for extra pages
    parameter MCONTR_LINTILE_RST_FRAME =        8, // reset frame number 
    parameter MCONTR_LINTILE_SINGLE =           9, // read/write a single page 
59 60
    parameter MCONTR_LINTILE_REPEAT =          10,  // read/write pages until disabled 
    parameter MCONTR_LINTILE_DIS_NEED =        11   // disable 'need' request 
61
)(
Andrey Filippov's avatar
Andrey Filippov committed
62
    input                          mrst,
63 64 65 66 67 68 69 70 71 72 73 74 75
    input                          mclk,
// 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
    
    output                   [7:0] status_ad,     // byte-wide address/data
    output                         status_rq,     // request to send downstream (last byte with rq==0)
    input                          status_start,   // acknowledge of address (first byte) from downsteram   

    input                          frame_start,   // resets page, x,y, and initiates transfer requests (in write mode will wait for next_page)
    input                          next_page,     // page was read/written from/to 4*1kB on-chip buffer
//    output                         page_ready,    // == xfer_done, connect externally | Single-cycle pulse indicating that a page was read/written from/to DDR3 memory
    output                         frame_done,    // single-cycle pulse when the full frame (window) was transferred to/from DDR3 memory
76
    output                         frame_finished,// turns on and stays on after frame_done
77
// optional I/O for channel synchronization
78 79
// after the last tile in a frame, before starting a new frame line_unfinished will point to non-existent (too high) line in the same frame
    output [FRAME_HEIGHT_BITS-1:0] line_unfinished, // number of the current (unfinished ) line, RELATIVE TO FRAME, NOT WINDOW?. 
80
    input                          suspend,       // suspend transfers (from external line number comparator)
81
    output   [LAST_FRAME_BITS-1:0] frame_number,  // current frame number (for multi-frame ranges)
82 83 84
    output                         xfer_want,     // "want" data transfer
    output                         xfer_need,     // "need" - really need a transfer (only 1 page/ room for 1 page left in a buffer), want should still be set.
    input                          xfer_grant,    // sequencer programming access granted, deassert wait/need 
85 86
    output                         xfer_start_rd, // initiate a transfer (next cycle after xfer_grant)
    output                         xfer_start_wr, // initiate a transfer (next cycle after xfer_grant)
87 88 89 90
    output                   [2:0] xfer_bank,     // bank address
    output    [ADDRESS_NUMBER-1:0] xfer_row,      // memory row
    output    [COLADDR_NUMBER-4:0] xfer_col,      // start memory column in 8-bursts
    output     [NUM_XFER_BITS-1:0] xfer_num128,   // number of 128-bit words to transfer (8*16 bits) - full bursts of 8 ( 0 - maximal length, 64)
91
    output                         xfer_partial,  // partial tile (first of 2) , sequencer will not generate page_next at the end of block   
92
    input                          xfer_done,     // transfer to/from the buffer finished
93
    output                         xfer_page_rst_wr, // reset buffer internal page - at each frame start or when specifically reset (write to memory channel), @posedge
94 95
    output                         xfer_page_rst_rd, // reset buffer internal page - at each frame start or when specifically reset (read memory channel), @negedge
    output                         cmd_wrmem
96 97 98
);
    localparam NUM_RC_BURST_BITS=ADDRESS_NUMBER+COLADDR_NUMBER-3;  //to spcify row and col8 == 22
    localparam MPY_WIDTH=        NUM_RC_BURST_BITS; // 22
Andrey Filippov's avatar
Andrey Filippov committed
99
    localparam PAR_MOD_LATENCY=  9; // TODO: Find actual worst-case latency for:
100 101 102
    reg    [FRAME_WIDTH_BITS-1:0] curr_x;         // (calculated) start of transfer x (relative to window left)
    reg   [FRAME_HEIGHT_BITS-1:0] curr_y;         // (calculated) start of transfer y (relative to window top)
    reg     [FRAME_HEIGHT_BITS:0] next_y;         // (calculated) next row number
Andrey Filippov's avatar
Andrey Filippov committed
103
    reg   [NUM_RC_BURST_BITS-1:0] line_start_addr;// (calculated) Line start (in {row,col8} in burst8
104
 // calculating full width from the frame width
105 106 107
// WARNING: [Synth 8-3936] Found unconnected internal register 'frame_y_reg' and it is trimmed from '16' to '3' bits. [memctrl/mcntrl_linear_rw.v:268]
// Throblem seems to be that frame_y8_r_reg (load of trimmed bits of the frame_y_reg) is (as intended) absorbed into DSP48. The lower 3 bits are used
// outside of the DSP 48.  "dont_touch" seems to work here
108 109 110 111
`ifndef IGNORE_ATTR
    (* keep = "true" *)
`endif    
    reg   [FRAME_HEIGHT_BITS-1:0] frame_y;     // current line number referenced to the frame top
112 113 114 115 116
    reg    [FRAME_WIDTH_BITS-1:0] frame_x;     // current column number referenced to the frame left
    reg   [FRAME_HEIGHT_BITS-4:0] frame_y8_r;  // (13 bits) current row with bank removed, latency2 (to be absorbed when inferred DSP multipler)
    reg      [FRAME_WIDTH_BITS:0] frame_full_width_r;  // (14 bit) register to be absorbed by MPY
    reg           [MPY_WIDTH-1:0] mul_rslt;
    reg   [NUM_RC_BURST_BITS-1:0] start_addr_r;   // 22 bit - to be absorbed by DSP
117 118
//    reg                     [2:0] bank_reg [2:0];
    reg             [3 * 3 - 1:0] bank_reg;
119 120 121 122
    wire [FRAME_WIDTH_BITS+FRAME_HEIGHT_BITS-3:0] mul_rslt_w;
    reg      [FRAME_WIDTH_BITS:0] row_left;   // number of 8-bursts left in the current row
    reg                           last_in_row;
    reg      [COLADDR_NUMBER-3:0] mem_page_left; // number of 8-bursts left in the pointed memory page
Andrey Filippov's avatar
Andrey Filippov committed
123
    reg      [COLADDR_NUMBER-4:0] line_start_page_left; // number of 8-burst left in the memory page from the start of the frame line
124
    reg         [NUM_XFER_BITS:0] lim_by_xfer;   // number of bursts left limited by the longest transfer (currently 64)
125 126 127 128 129 130 131
//    reg        [MAX_TILE_WIDTH:0] lim_by_tile_width;     // number of bursts left limited by the longest transfer (currently 64)
    wire     [COLADDR_NUMBER-3:0] remainder_in_xfer ;//remainder_tile_width;  // number of bursts postponed to the next partial tile (because of the page crossing) MSB-sign
    reg                           continued_xfer;   //continued_tile;        // this is a continued tile (caused by page crossing) - only once
    reg       [NUM_XFER_BITS-1:0] leftover; //[MAX_TILE_WIDTH-1:0] leftover_cols;         // valid with continued_tile, number of columns left
    
    
    
132
    reg         [NUM_XFER_BITS:0] xfer_num128_r;   // number of 128-bit words to transfer (8*16 bits) - full bursts of 8
133
//    reg       [NUM_XFER_BITS-1:0] xfer_num128_m1_r;   // number of 128-bit words to transfer minus 1 (8*16 bits) - full bursts of 8
134
    wire                          pgm_param_w;  // program one of the parameters, invalidate calculated results for PAR_MOD_LATENCY
135 136 137
    reg                     [2:0] xfer_start_r; // 1 hot started by xfer start only (not by parameter change)
    reg                           xfer_start_rd_r;
    reg                           xfer_start_wr_r;
138
    reg     [PAR_MOD_LATENCY-1:0] par_mod_r;
139
    reg     [PAR_MOD_LATENCY-1:0] recalc_r; // 1-hot CE for re-calculating registers
140
    wire                          calc_valid;   // calculated registers have valid values   
141
    wire                          chn_en;   // enable requests by channel (continue ones in progress), enable frame_start inputs
142
    wire                          chn_rst; // resets command, including fifo;
143
    reg                           chn_rst_d; // delayed by 1 cycle do detect turning off
144 145 146 147 148
//    reg                           xfer_reset_page_r;
    reg                           xfer_page_rst_r=1;
    reg                           xfer_page_rst_pos=1;  
    reg                           xfer_page_rst_neg=1;  
    
149 150
    reg                     [2:0] page_cntr;
    
151
//    wire                          cmd_wrmem; //=MCNTRL_SCANLINE_WRITE_MODE; // 0: read from memory, 1:write to memory
152
    wire                    [1:0] cmd_extra_pages; // external module needs more than 1 page
153
    wire                          disable_need; // do not assert need, only want
154 155 156 157
    wire                          repeat_frames; // mode bit
    wire                          single_frame_w; // pulse
    wire                          rst_frame_num_w;
    reg                           single_frame_r;  // pulse
158
    reg                     [1:0] rst_frame_num_r; // reset frame number/next start address
159 160
    reg                           frame_en;       // enable next frame
    
161 162 163 164
    reg                           busy_r;
    reg                           want_r;
    reg                           need_r;
    reg                           frame_done_r;
165
    reg                           frame_finished_r;    
166 167 168 169 170
    wire                          last_in_row_w;
    wire                          last_row_w;
    reg                           last_block;
    reg [MCNTRL_SCANLINE_PENDING_CNTR_BITS-1:0] pending_xfers; // number of requested,. but not finished block transfers      
    reg   [NUM_RC_BURST_BITS-1:0] row_col_r;
171 172 173 174
//    reg [2*FRAME_HEIGHT_BITS-1:0] line_unfinished_r;
    reg   [FRAME_HEIGHT_BITS-1:0] line_unfinished_relw_r;
    reg   [FRAME_HEIGHT_BITS-1:0] line_unfinished_r;
    
175 176 177 178 179 180 181 182 183
    wire                          pre_want;
    wire                    [1:0] status_data;
    wire                    [3:0] cmd_a; 
    wire                   [31:0] cmd_data; 
    wire                          cmd_we;
    
    wire                          set_mode_w;
    wire                          set_status_w;
    wire                          set_start_addr_w;
184 185
    wire                          set_frame_size_w;
    wire                          set_last_frame_w;
186 187 188 189
    wire                          set_frame_width_w;
    wire                          set_window_wh_w;
    wire                          set_window_x0y0_w;
    wire                          set_window_start_w;
Andrey Filippov's avatar
Andrey Filippov committed
190 191
    wire                          lsw13_zero=!(|cmd_data[FRAME_WIDTH_BITS-1:0]); // LSW 13 (FRAME_WIDTH_BITS) low bits are all 0 - set carry bit  
    wire                          msw_zero=  !(|cmd_data[31:16]); // MSW all bits are 0 - set carry bit
192 193
      
    
194
    reg                    [11:0] mode_reg;//mode register: {dis_need,repet,single,rst_frame,na[2:0],extra_pages[1:0],write_mode,enable,!reset}
195 196 197 198
    
    reg   [NUM_RC_BURST_BITS-1:0] start_range_addr; // (programmed) First frame in range start (in {row,col8} in burst8, bank ==0
    reg   [NUM_RC_BURST_BITS-1:0] frame_size;       // (programmed) First frame in range start (in {row,col8} in burst8, bank ==0
    reg     [LAST_FRAME_BITS-1:0] last_frame_number; 
199
    reg   [NUM_RC_BURST_BITS-1:0] start_addr;     // (programmed) Frame start (in {row,col8} in burst8, bank ==0
200 201
    reg   [NUM_RC_BURST_BITS-1:0] next_frame_start_addr;
    reg     [LAST_FRAME_BITS-1:0] frame_number_cntr;
202 203
    reg     [LAST_FRAME_BITS-1:0] frame_number_current;
    
204
    reg                           is_last_frame;
205 206
//    reg                     [2:0] frame_start_r;
    reg                     [4:0] frame_start_r; // increased length to have time from line_unfinished to suspend (external)
207 208 209 210 211 212 213 214 215 216
    
    reg      [FRAME_WIDTH_BITS:0] frame_full_width;     // (programmed) increment combined row/col when moving to the next line
                                                  // frame_width rounded up to max transfer (half page) if frame_width> max transfer/2,
                                                  // otherwise (smaller widths) round up to the nearest power of 2
    reg      [FRAME_WIDTH_BITS:0] window_width;   // (programmed) 0- max
    reg     [FRAME_HEIGHT_BITS:0] window_height;  // (programmed) 0- max
    reg    [FRAME_WIDTH_BITS-1:0] window_x0;      // (programmed) window left
    reg   [FRAME_HEIGHT_BITS-1:0] window_y0;      // (programmed) window top
    reg    [FRAME_WIDTH_BITS-1:0] start_x;        // (programmed) normally 0, copied to curr_x on frame_start  
    reg   [FRAME_HEIGHT_BITS-1:0] start_y;        // (programmed) normally 0, copied to curr_y on frame_start 
217
    reg                           xfer_done_d;    // xfer_done delayed by 1 cycle;
218
    assign frame_number =       frame_number_current;
219
    
220 221 222
    assign set_mode_w =         cmd_we && (cmd_a== MCNTRL_SCANLINE_MODE);
    assign set_status_w =       cmd_we && (cmd_a== MCNTRL_SCANLINE_STATUS_CNTRL);
    assign set_start_addr_w =   cmd_we && (cmd_a== MCNTRL_SCANLINE_STARTADDR);
223 224
    assign set_frame_size_w =   cmd_we && (cmd_a== MCNTRL_SCANLINE_FRAME_SIZE);
    assign set_last_frame_w =   cmd_we && (cmd_a== MCNTRL_SCANLINE_FRAME_LAST);
225 226 227 228
    assign set_frame_width_w =  cmd_we && (cmd_a== MCNTRL_SCANLINE_FRAME_FULL_WIDTH);
    assign set_window_wh_w =    cmd_we && (cmd_a== MCNTRL_SCANLINE_WINDOW_WH);
    assign set_window_x0y0_w =  cmd_we && (cmd_a== MCNTRL_SCANLINE_WINDOW_X0Y0);
    assign set_window_start_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_WINDOW_STARTXY);
229
    
230 231
    assign single_frame_w =  cmd_we && (cmd_a== MCNTRL_SCANLINE_MODE) && cmd_data[MCONTR_LINTILE_SINGLE];
    assign rst_frame_num_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_MODE) && cmd_data[MCONTR_LINTILE_RST_FRAME];
232
    
233
    // Set parameter registers
Andrey Filippov's avatar
Andrey Filippov committed
234 235
    always @(posedge mclk) begin
        if      (mrst)               mode_reg <= 0;
236
        else if (set_mode_w)         mode_reg <= cmd_data[11:0]; // 4:0]; // [4:0];
237

Andrey Filippov's avatar
Andrey Filippov committed
238 239
        if (mrst) single_frame_r <= 0;
        else      single_frame_r <= single_frame_w;
240
        
Andrey Filippov's avatar
Andrey Filippov committed
241 242
        if (mrst) rst_frame_num_r <= 0;
        else      rst_frame_num_r <= {rst_frame_num_r[0],
243 244 245 246 247
                                     rst_frame_num_w |
                                     set_start_addr_w |
                                     set_last_frame_w |
                                     set_frame_size_w};

Andrey Filippov's avatar
Andrey Filippov committed
248
        if      (mrst)               start_range_addr <= 0;
249 250
        else if (set_start_addr_w)   start_range_addr <= cmd_data[NUM_RC_BURST_BITS-1:0];

Andrey Filippov's avatar
Andrey Filippov committed
251
        if      (mrst)               frame_size <= 0;
252 253
        else if (set_start_addr_w)   frame_size <= 1; // default number of frames - just one
        else if (set_frame_size_w)   frame_size <= cmd_data[NUM_RC_BURST_BITS-1:0];
254

Andrey Filippov's avatar
Andrey Filippov committed
255
        if      (mrst)             last_frame_number <= 0;
256 257
        else if (set_last_frame_w) last_frame_number <= cmd_data[LAST_FRAME_BITS-1:0];
        
Andrey Filippov's avatar
Andrey Filippov committed
258
        if      (mrst)              frame_full_width <=  0;
Andrey Filippov's avatar
Andrey Filippov committed
259
        else if (set_frame_width_w) frame_full_width <= {lsw13_zero,cmd_data[FRAME_WIDTH_BITS-1:0]};
260
        
Andrey Filippov's avatar
Andrey Filippov committed
261 262
        if (mrst) is_last_frame <= 0;
        else      is_last_frame <= frame_number_cntr == last_frame_number;
263
        
Andrey Filippov's avatar
Andrey Filippov committed
264 265
        if (mrst) frame_start_r <= 0;
        else      frame_start_r <= {frame_start_r[3:0], frame_start & frame_en};
266

Andrey Filippov's avatar
Andrey Filippov committed
267
        if      (mrst)                            frame_en <= 0;
268 269 270
        else if (single_frame_r || repeat_frames) frame_en <= 1;
        else if (frame_start)                     frame_en <= 0;
        
Andrey Filippov's avatar
Andrey Filippov committed
271
        if      (mrst)               frame_number_cntr <= 0;
272 273 274
        else if (rst_frame_num_r[0]) frame_number_cntr <= 0;
        else if (frame_start_r[2])   frame_number_cntr <= is_last_frame?{LAST_FRAME_BITS{1'b0}}:(frame_number_cntr+1);

Andrey Filippov's avatar
Andrey Filippov committed
275
        if      (mrst)               frame_number_current <= 0;
276 277 278
        else if (rst_frame_num_r[0]) frame_number_current <= 0;
        else if (frame_start_r[2])   frame_number_current <= frame_number_cntr;

Andrey Filippov's avatar
Andrey Filippov committed
279
        if      (mrst)               next_frame_start_addr <= start_range_addr; // just to use rst
280 281 282
        else if (rst_frame_num_r[1]) next_frame_start_addr <= start_range_addr;
        else if (frame_start_r[2])   next_frame_start_addr <= is_last_frame? start_range_addr : (start_addr+frame_size);

Andrey Filippov's avatar
Andrey Filippov committed
283
        if      (mrst)               start_addr <= start_range_addr; // just to use rst
284 285
        else if (frame_start_r[0])   start_addr <= next_frame_start_addr;
        
Andrey Filippov's avatar
Andrey Filippov committed
286
        if      (mrst) begin
287 288 289 290 291 292 293
               window_width <= 0; 
               window_height <=  0;
        end else if (set_window_wh_w)  begin
               window_width <= {lsw13_zero,cmd_data[FRAME_WIDTH_BITS-1:0]};
               window_height  <= {msw_zero,cmd_data[FRAME_HEIGHT_BITS+15:16]};
        end

Andrey Filippov's avatar
Andrey Filippov committed
294
        if      (mrst) begin
295 296 297 298 299 300 301
               window_x0 <= 0; 
               window_y0 <=  0;
        end else if (set_window_x0y0_w)  begin
               window_x0 <= cmd_data[FRAME_WIDTH_BITS-1:0];
               window_y0  <=cmd_data[FRAME_HEIGHT_BITS+15:16];
        end

Andrey Filippov's avatar
Andrey Filippov committed
302
        if      (mrst) begin
303 304 305 306 307 308 309 310 311
               start_x <= 0; 
               start_y <=  0;
        end else if (set_window_start_w)  begin
               start_x <= cmd_data[FRAME_WIDTH_BITS-1:0];
               start_y  <=cmd_data[FRAME_HEIGHT_BITS+15:16];
        end
    end
    assign mul_rslt_w=  frame_y8_r * frame_full_width_r; // 5 MSBs will be discarded
    assign xfer_num128= xfer_num128_r[NUM_XFER_BITS-1:0];
312 313
    assign xfer_start_rd=  xfer_start_rd_r;
    assign xfer_start_wr=  xfer_start_wr_r;
314
    assign calc_valid=  par_mod_r[PAR_MOD_LATENCY-1]; // MSB, longest 0
315 316 317
    assign xfer_page_rst_wr=  xfer_page_rst_r;
    assign xfer_page_rst_rd=  xfer_page_rst_neg;
    
318 319
    assign xfer_partial=      xfer_limited_by_mem_page_r;
    
320
    assign frame_done=  frame_done_r;
321 322
    assign frame_finished=  frame_finished_r;
    
323 324
//    assign pre_want=    chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !last_block && !suspend && !frame_start_r[0];
    assign pre_want=    chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !last_block && !suspend && !(|frame_start_r);
325

326 327 328 329
    assign last_in_row_w=(row_left=={{(FRAME_WIDTH_BITS-NUM_XFER_BITS){1'b0}},xfer_num128_r});
    assign last_row_w=  next_y==window_height;
    assign xfer_want=   want_r;
    assign xfer_need=   need_r;
330
    assign xfer_bank=   bank_reg[3 * 2 +: 3]; // TODO: just a single reg layer
331 332
    assign xfer_row= row_col_r[NUM_RC_BURST_BITS-1:COLADDR_NUMBER-3] ;      // memory row
    assign xfer_col= row_col_r[COLADDR_NUMBER-4:0];    // start memory column in 8-bursts
333
    assign line_unfinished = line_unfinished_r; // [FRAME_HEIGHT_BITS +: FRAME_HEIGHT_BITS];
334 335 336 337 338
    assign chn_en =         mode_reg[MCONTR_LINTILE_NRESET] & mode_reg[MCONTR_LINTILE_EN];   // enable requests by channel (continue ones in progress)
    assign chn_rst =        ~mode_reg[MCONTR_LINTILE_NRESET]; // resets command, including fifo;
    assign cmd_wrmem =       mode_reg[MCONTR_LINTILE_WRITE];// 0: read from memory, 1:write to memory
    assign cmd_extra_pages = mode_reg[MCONTR_LINTILE_EXTRAPG+:MCONTR_LINTILE_EXTRAPG_BITS]; // external module needs more than 1 page
    assign repeat_frames=    mode_reg[MCONTR_LINTILE_REPEAT];
339
    assign disable_need =    mode_reg[MCONTR_LINTILE_DIS_NEED];
340
    
341
    assign status_data= {frame_finished_r, busy_r};     // TODO: Add second bit?
342
    assign pgm_param_w=      cmd_we;
343 344
    localparam [COLADDR_NUMBER-3-NUM_XFER_BITS-1:0] EXTRA_BITS=0;
    assign remainder_in_xfer = {EXTRA_BITS, lim_by_xfer}-mem_page_left;
345 346
    
    integer i;
347 348 349
    wire xfer_limited_by_mem_page;
    reg  xfer_limited_by_mem_page_r;
    assign xfer_limited_by_mem_page= mem_page_left < {EXTRA_BITS,lim_by_xfer};
350

351
/// Recalcualting just after starting request - preparing for the next one. Also happens after parameter change.
352
/// Should dpepend only on the parameters updated separately (curr_x, curr_y)
353
    always @(posedge mclk) begin // TODO: Match latencies (is it needed?) Reduce consumption by CE?
354 355 356 357
        if (recalc_r[0]) begin // cycle 1
            frame_x <= curr_x + window_x0;
            frame_y <= curr_y + window_y0;
            next_y <= curr_y + 1;
Andrey Filippov's avatar
Andrey Filippov committed
358
            row_left <= window_width - curr_x; // 14 bits - 13 bits
359 360
        end
// registers to be absorbed in DSP block        
361 362 363 364 365
        frame_y8_r <= frame_y[FRAME_HEIGHT_BITS-1:3]; // lat=2
        frame_full_width_r <= frame_full_width;
        start_addr_r <= start_addr;
        mul_rslt <= mul_rslt_w[MPY_WIDTH-1:0]; // frame_y8_r * frame_width_r; // 7 bits will be discarded lat=3;
        line_start_addr <= start_addr_r+mul_rslt; // lat=4
Andrey Filippov's avatar
Andrey Filippov committed
366
        
367 368 369
// TODO: Verify MPY/register timing above        
        if (recalc_r[5]) begin // cycle 6
            row_col_r <= line_start_addr+frame_x;
Andrey Filippov's avatar
Andrey Filippov committed
370 371
//            line_start_page_left <= {COLADDR_NUMBER-3{1'b0}} - line_start_addr[COLADDR_NUMBER-4:0]; // 7 bits
            line_start_page_left <=  - line_start_addr[COLADDR_NUMBER-4:0]; // 7 bits
372
        end
373
        bank_reg[0 +:3]   <= frame_y[2:0]; //TODO: is it needed - a pipeline for the bank? - remove! 
374
        for (i=0;i<2; i = i+1)
375
            bank_reg[(i+1)*3 +:3] <= bank_reg[i * 3 +: 3];
376
            
Andrey Filippov's avatar
Andrey Filippov committed
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
            
        if (recalc_r[6]) begin // cycle 7
            mem_page_left <= {1'b1,line_start_page_left} - frame_x[COLADDR_NUMBER-4:0];
            
            lim_by_xfer <= (|row_left[FRAME_WIDTH_BITS:NUM_XFER_BITS])?
                (1<<NUM_XFER_BITS):
                row_left[NUM_XFER_BITS:0]; // 7 bits, max 'h40
        end
        if (recalc_r[7]) begin // cycle 8
            xfer_limited_by_mem_page_r <= xfer_limited_by_mem_page && !continued_xfer;     
            xfer_num128_r<= continued_xfer?
                {EXTRA_BITS,leftover}:
                (xfer_limited_by_mem_page?
                     mem_page_left[NUM_XFER_BITS:0]:
                     lim_by_xfer[NUM_XFER_BITS:0]);
            //xfer_num128_r depends on leftover only if continued_xfer (after first shortened actual xfer and will not change w/o xfers)
            // and (next) leftover is only set  if continued_xfer==0, so multiple runs without chnge of continued_xfer will not differ       
            if (!continued_xfer) leftover <= remainder_in_xfer[NUM_XFER_BITS-1:0]; //  {EXTRA_BITS, lim_by_xfer}-mem_page_left;
        end
        
        if (recalc_r[8]) begin // cycle 9
            last_in_row <= last_in_row_w; //(row_left=={{(FRAME_WIDTH_BITS-NUM_XFER_BITS){1'b0}},xfer_num128_r});
        end
            
            
402
    end
403
wire    start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;    
404 405
// now have row start address, bank and row_left ;
// calculate number to read (min of row_left, maximal xfer and what is left in the DDR3 page    
Andrey Filippov's avatar
Andrey Filippov committed
406 407
    always @(posedge mclk) begin
        if      (mrst)                par_mod_r<=0;
408 409 410 411 412
        else if (pgm_param_w ||
                 xfer_start_r[0] ||
                 chn_rst ||
                 frame_start_r[0])    par_mod_r<=0;
        else                          par_mod_r <= {par_mod_r[PAR_MOD_LATENCY-2:0], 1'b1};
413

Andrey Filippov's avatar
Andrey Filippov committed
414
        if      (mrst)         chn_rst_d <= 0;
415 416
        else                   chn_rst_d <= chn_rst;

Andrey Filippov's avatar
Andrey Filippov committed
417
        if      (mrst)         recalc_r<=0;
418
        else if (chn_rst)      recalc_r<=0;
419
        else                   recalc_r <= {recalc_r[PAR_MOD_LATENCY-2:0],
420
             ((xfer_start_r[0] | frame_start_r[0]) & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
421
        
Andrey Filippov's avatar
Andrey Filippov committed
422
        if      (mrst)              busy_r <= 0;
423 424 425
        else if (chn_rst)           busy_r <= 0;
        else if (frame_start_r[0])  busy_r <= 1;
        else if (frame_done_r)      busy_r <= 0;
426
        
Andrey Filippov's avatar
Andrey Filippov committed
427
        if (mrst)         xfer_done_d <= 0;
428 429
        else              xfer_done_d <= xfer_done;
        
430
        
Andrey Filippov's avatar
Andrey Filippov committed
431
        if (mrst)                   continued_xfer <= 1'b0;
432 433 434
        else if (chn_rst)           continued_xfer <= 1'b0;
        else if (frame_start_r[0])  continued_xfer <= 1'b0;
        else if (xfer_start_r[0])   continued_xfer <= xfer_limited_by_mem_page_r; // only set after actual start if it was partial, not after parameter change
435

436
        // single cycle (sent out)
Andrey Filippov's avatar
Andrey Filippov committed
437
        if (mrst)         frame_done_r <= 0;
438
        else              frame_done_r <= busy_r && last_block && xfer_done_d && (pending_xfers==0);
439

440
        // turns and stays on (used in status)
Andrey Filippov's avatar
Andrey Filippov committed
441
        if (mrst)                             frame_finished_r <= 0;
442 443
        else if (chn_rst || frame_start_r[0]) frame_finished_r <= 0;
        else if (frame_done_r)                frame_finished_r <= 1;
444
        
Andrey Filippov's avatar
Andrey Filippov committed
445 446
        if (mrst) xfer_start_r <= 0;
        else      xfer_start_r <= {xfer_start_r[1:0],xfer_grant && !chn_rst};
447

Andrey Filippov's avatar
Andrey Filippov committed
448 449
        if (mrst) xfer_start_rd_r <= 0;
        else      xfer_start_rd_r <=  xfer_grant && !chn_rst && !cmd_wrmem;
450

Andrey Filippov's avatar
Andrey Filippov committed
451 452
        if (mrst) xfer_start_wr_r <= 0;
        else      xfer_start_wr_r <=  xfer_grant && !chn_rst && cmd_wrmem;
453
        
454
        if (mrst || disable_need)                         need_r <= 0;
455 456
        else if (chn_rst || xfer_grant)                   need_r <= 0;
        else if ((pre_want  || want_r) && (page_cntr>=3)) need_r <= 1; // may raise need if want was already set
457

Andrey Filippov's avatar
Andrey Filippov committed
458
        if (mrst)                                                want_r <= 0;
459 460 461
        else if (chn_rst || xfer_grant)                          want_r <= 0;
        else if (pre_want && (page_cntr>{1'b0,cmd_extra_pages})) want_r <= 1;
        
Andrey Filippov's avatar
Andrey Filippov committed
462
        if (mrst)                                  page_cntr <= 0;
463
        else if (frame_start_r[0])                 page_cntr <= cmd_wrmem?0:4; // What about last pages (like if only 1 page is needed)? Early frame end?
464 465
        else if ( start_not_partial && !next_page) page_cntr <= page_cntr - 1;     
        else if (!start_not_partial &&  next_page) page_cntr <= page_cntr + 1;
466
        
Andrey Filippov's avatar
Andrey Filippov committed
467 468
        if (mrst) xfer_page_rst_r <= 1;
        else      xfer_page_rst_r <= chn_rst || (MCNTRL_SCANLINE_FRAME_PAGE_RESET ? (frame_start_r[0] & cmd_wrmem):1'b0);
469

Andrey Filippov's avatar
Andrey Filippov committed
470 471
        if (mrst) xfer_page_rst_pos <= 1;
        else      xfer_page_rst_pos <= chn_rst || (MCNTRL_SCANLINE_FRAME_PAGE_RESET ? (frame_start_r[0] & ~cmd_wrmem):1'b0);
472

473 474
        
// increment x,y (two cycles)
475
        if (mrst)                                 curr_x <= 0;
476
        else if (chn_rst || frame_start_r[0])     curr_x <= start_x;
477 478
        else if (xfer_start_r[0])                 curr_x <= last_in_row?0: curr_x + xfer_num128_r;
        
479
        if (mrst)                                 curr_y <= 0;
480
        else if (chn_rst || frame_start_r[0])     curr_y <= start_y;
481 482
        else if (xfer_start_r[0] && last_in_row)  curr_y <= next_y[FRAME_HEIGHT_BITS-1:0];
               
483 484 485
        if      (mrst)                            last_block <= 0;
        else if (chn_rst || !busy_r)              last_block <= 0;
        else if (xfer_start_r[0])                 last_block <= last_row_w && last_in_row_w;
486
        
487 488
        if      (mrst)                           pending_xfers <= 0;
        else if (chn_rst || !busy_r)             pending_xfers <= 0;
Andrey Filippov's avatar
Andrey Filippov committed
489 490
        else if ( xfer_start_r[0] && !xfer_done) pending_xfers <= pending_xfers + 1;     
        else if (!xfer_start_r[0] &&  xfer_done) pending_xfers <= pending_xfers - 1;
491
        
492
/*        
493
        //line_unfinished_r cmd_wrmem
494 495 496
        if (mrst)                              line_unfinished_r[0 +: FRAME_HEIGHT_BITS] <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
        else if (chn_rst || frame_start_r[0]) line_unfinished_r[0 +: FRAME_HEIGHT_BITS] <= window_y0+start_y;
        else if (xfer_start_r[2])             line_unfinished_r[0 +: FRAME_HEIGHT_BITS] <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
497

498
        if (mrst)                              line_unfinished_r[FRAME_HEIGHT_BITS +: FRAME_HEIGHT_BITS] <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
499
//        else if (chn_rst || frame_start_r[0]) line_unfinished_r[1] <= window_y0+start_y;
500
        else if (chn_rst || frame_start_r[2]) line_unfinished_r[FRAME_HEIGHT_BITS +: FRAME_HEIGHT_BITS] <= window_y0+start_y; // _r[0] -> _r[2] to make it simultaneous with frame_number
501
        // in read mode advance line number ASAP
502
        else if (xfer_start_r[2] && !cmd_wrmem) line_unfinished_r[FRAME_HEIGHT_BITS +: FRAME_HEIGHT_BITS] <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
503
        // in write mode advance line number only when it is guaranteed it will be the first to actually access memory
504 505 506 507 508 509 510
        else if (xfer_grant      && cmd_wrmem)  line_unfinished_r[FRAME_HEIGHT_BITS +: FRAME_HEIGHT_BITS] <= line_unfinished_r[0 +: FRAME_HEIGHT_BITS];
*/        
/* 
        if (mrst)                                                line_unfinished_relw_r <= 0;
        else if (cmd_wrmem && (frame_start_r[1] || !chn_en))     line_unfinished_relw_r <= start_y;
        else if ((!cmd_wrmem && recalc_r[1]) || xfer_start_r[2]) line_unfinished_relw_r <= next_y[FRAME_HEIGHT_BITS-1:0];
        //  xfer_start_r[2] and recalc_r[1] are at the same time
511
        
512 513 514 515 516 517 518 519 520
        if (mrst || (frame_start || !chn_en))  line_unfinished_r <= {FRAME_HEIGHT_BITS{~cmd_wrmem}}; // lowest/highest value until valid
        else if (recalc_r[2])                  line_unfinished_r <= line_unfinished_relw_r + window_y0;
*/
        if (recalc_r[0]) line_unfinished_relw_r <= curr_y + (cmd_wrmem ? 0: 1);
        
        if (mrst || (frame_start || !chn_en))  line_unfinished_r <= {FRAME_HEIGHT_BITS{~cmd_wrmem}}; // lowest/highest value until valid
        else if (recalc_r[2])                  line_unfinished_r <= line_unfinished_relw_r + window_y0;


521
    end
522 523 524
    always @ (negedge mclk) begin
        xfer_page_rst_neg <= xfer_page_rst_pos;
    end
525 526 527 528 529 530 531
    cmd_deser #(
        .ADDR       (MCNTRL_SCANLINE_ADDR),
        .ADDR_MASK  (MCNTRL_SCANLINE_MASK),
        .NUM_CYCLES (6),
        .ADDR_WIDTH (4),
        .DATA_WIDTH (32)
    ) cmd_deser_32bit_i (
Andrey Filippov's avatar
Andrey Filippov committed
532 533 534 535 536 537 538 539
        .rst        (1'b0),      //rst), // input
        .clk        (mclk),      // input
        .srst       (mrst),      // input
        .ad         (cmd_ad),    // input[7:0] 
        .stb        (cmd_stb),   // input
        .addr       (cmd_a),     // output[15:0] 
        .data       (cmd_data),  // output[31:0] 
        .we         (cmd_we)     // output
540 541 542 543 544 545
    );

    status_generate #(
        .STATUS_REG_ADDR  (MCNTRL_SCANLINE_STATUS_REG_ADDR),
        .PAYLOAD_BITS     (2)
    ) status_generate_i (
Andrey Filippov's avatar
Andrey Filippov committed
546 547 548 549
        .rst              (1'b0),          //rst), // input
        .clk              (mclk),          // input
        .srst             (mrst),          // input
        .we               (set_status_w),  // input
550
        .wd               (cmd_data[7:0]), // input[7:0] 
Andrey Filippov's avatar
Andrey Filippov committed
551 552 553 554
        .status           (status_data),   // input[25:0] 
        .ad               (status_ad),     // output[7:0] 
        .rq               (status_rq),     // output
        .start            (status_start)   // input
555 556 557
    );
endmodule