membridge.v 35.8 KB
Newer Older
1 2 3
/*******************************************************************************
 * Module: membridge
 * Date:2015-04-26  
4
 * Author: Andrey Filippov     
5 6
 * Description: bi-directional bridge between system and video memory over axi_hp
 *
7
 * Copyright (c) 2015 Elphel, Inc.
8 9 10 11 12 13 14 15 16 17 18 19
 * membridge.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.
 *
 *  membridge.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/> .
20 21 22 23 24 25
 *
 * Additional permission under GNU GPL version 3 section 7:
 * If you modify this Program, or any covered work, by linking or combining it
 * with independent modules provided by the FPGA vendor only (this permission
 * does not extend to any 3-rd party modules, "soft cores" or macros) under
 * different license terms solely for the purpose of generating binary "bitstream"
26
 * files and/or simulating the code, the copyright holders of this Program give
27 28
 * you the right to distribute the covered work without those independent modules
 * as long as the source code for them is available from the FPGA vendor free of
Andrey Filippov's avatar
Andrey Filippov committed
29
 * charge, and there is no dependence on any encrypted modules for simulating of
30 31 32
 * the combined code. This permission applies to you if the distributed code
 * contains all the components and scripts required to completely simulate it
 * with at least one of the Free Software programs.
33 34
 *******************************************************************************/
`timescale 1ns/1ps
35
//`define MEMBRIDGE_DEBUG_READ 1
36 37
module  membridge#(
    parameter MEMBRIDGE_ADDR=                     'h200,
38
    parameter MEMBRIDGE_MASK=                     'h7f0,
Andrey Filippov's avatar
Andrey Filippov committed
39
    parameter MEMBRIDGE_CTRL=                     'h0, // bit 0 - enable, bits[2:1]: 11 - start(continue), 01 - start and reset address
40 41 42 43 44 45
    parameter MEMBRIDGE_STATUS_CNTRL=             'h1,
    parameter MEMBRIDGE_LO_ADDR64=                'h2, // low address of the system memory, in 64-bit words (<<3 to get byte address)
    parameter MEMBRIDGE_SIZE64=                   'h3, // size of the system memory range (access will roll over to lo_addr
    parameter MEMBRIDGE_START64=                  'h4, // start address relative to lo_addr
    parameter MEMBRIDGE_LEN64=                    'h5, // full length of transfer in 64-bit words
    parameter MEMBRIDGE_WIDTH64=                  'h6, // frame width in 64-bit words (partial last page in each line)
46
    parameter MEMBRIDGE_MODE=                     'h7, // bits [3:0] - *_cache, bit [4] - cache debug
47 48 49 50
    parameter MEMBRIDGE_STATUS_REG=               'h3b,
    parameter FRAME_HEIGHT_BITS=                   16,   // Maximal frame height bits
    parameter FRAME_WIDTH_BITS=                    13
//    ,parameter MCNTRL_SCANLINE_FRAME_PAGE_RESET=  1'b0 // reset internal page number to zero at the frame start (false - only when hard/soft reset)
51 52 53
`ifdef DEBUG_RING
    ,parameter DEBUG_CMD_LATENCY = 2 
`endif        
54 55
    
)(
Andrey Filippov's avatar
Andrey Filippov committed
56 57 58 59 60 61
//    input         rst,
    input                         mrst, // @posedge mclk - sync reset
    input                         hrst, // @posedge hclk - sync reset
    
    input                         mclk, // for command/status
    input                         hclk,   // global clock to run axi_hp @ 150MHz
62 63 64 65 66 67 68 69 70
    // 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,   // status address/data - up to 5 bytes: A - {seq,status[1:0]} - status[2:9] - status[10:17] - status[18:25]
    output                        status_rq,   // input request to send status downstream
    input                         status_start, // Acknowledge of the first status packet byte (address)
    // mcntrl_linear_rw.v interface
    output                        frame_start_chn, // input
    output                        next_page_chn, // input
71
    input                         cmd_wrmem,      // @mclk - writing to DDR3 mode (0 - reading from DDR3)
72 73 74
    input                         page_ready_chn, // output single mclk
    input                         frame_done_chn, // output single mclk
    input [FRAME_HEIGHT_BITS-1:0] line_unfinished_chn1, // output[15:0] @SuppressThisWarning VEditor unused (yet)
Andrey Filippov's avatar
Andrey Filippov committed
75
    output                        suspend_chn1, //
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    // buffer interface, DDR3 memory read
    input                         xfer_reset_page_rd, // input
    input                         buf_wpage_nxt,     // input
    input                         buf_wr, // input
    input                  [63:0] buf_wdata,
    // buffer interface, DDR3 memory write
    input                         xfer_reset_page_wr, // input  @ posedge mclk
    input                         buf_rpage_nxt,
    input                         buf_rd,
    output                [63:0]  buf_rdata, 
    // axi_hp signals write channel
    // write address
    output  [31:0] afi_awaddr,
    output         afi_awvalid,
    input          afi_awready, // @SuppressThisWarning VEditor unused - used FIF0 level
    output  [ 5:0] afi_awid,
    output  [ 1:0] afi_awlock,
    output  [ 3:0] afi_awcache,
    output  [ 2:0] afi_awprot,
    output  [ 3:0] afi_awlen,
96
    output  [ 1:0] afi_awsize,
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
    output  [ 1:0] afi_awburst,
    output  [ 3:0] afi_awqos,
    // write data
    output  [63:0] afi_wdata,
    output         afi_wvalid,
    input          afi_wready,  // @SuppressThisWarning VEditor unused - used FIF0 level
    output  [ 5:0] afi_wid,
    output         afi_wlast,
    output  [ 7:0] afi_wstrb,
    // write response
    input          afi_bvalid,
    output         afi_bready,
    input   [ 5:0] afi_bid,      // @SuppressThisWarning VEditor unused
    input   [ 1:0] afi_bresp,    // @SuppressThisWarning VEditor unused
    // PL extra (non-AXI) signals
    input   [ 7:0] afi_wcount,
    input   [ 5:0] afi_wacount,
    output         afi_wrissuecap1en,
    // AXI_HP signals - read channel
    // read address
    output  [31:0] afi_araddr,
    output         afi_arvalid,
    input          afi_arready,  // @SuppressThisWarning VEditor unused - used FIF0 level
    output  [ 5:0] afi_arid,
    output  [ 1:0] afi_arlock,
    output  [ 3:0] afi_arcache,
    output  [ 2:0] afi_arprot,
    output  [ 3:0] afi_arlen,
125
    output  [ 1:0] afi_arsize,
126 127 128 129 130 131 132 133
    output  [ 1:0] afi_arburst,
    output  [ 3:0] afi_arqos,
    // read data
    input   [63:0] afi_rdata,
    input          afi_rvalid,
    output         afi_rready,
    input   [ 5:0] afi_rid,     // @SuppressThisWarning VEditor unused
    input          afi_rlast,   // @SuppressThisWarning VEditor unused
134
    input   [ 1:0] afi_rresp,   // @SuppressThisWarning VEditor unused
135 136 137 138
    // PL extra (non-AXI) signals
    input   [ 7:0] afi_rcount,
    input   [ 2:0] afi_racount,
    output         afi_rdissuecap1en
139 140 141 142 143
`ifdef DEBUG_RING       
    ,output                       debug_do, // output to the debug ring
     input                        debug_sl, // 0 - idle, (1,0) - shift, (1,1) - load // SuppressThisWarning VEditor - not used
     input                        debug_di  // input from the debug ring
`endif         
144 145
    
);
146 147
    localparam BUFWR_WE_WIDTH = 4; //2; // 4;
    localparam SAFE_RD_BITS =   3; //2; // 3;
148 149 150
    // Some constant signals:
    
    assign afi_awlock =        2'h0;
151
//    assign afi_awcache =       4'h3;
152
    assign afi_awprot =        3'h0;
153
    assign afi_awsize =        2'h3;
154 155 156 157 158 159
    assign afi_awburst =       2'h1;
    assign afi_awqos =         4'h0;
    assign afi_wstrb =         8'hff;
    assign afi_wrissuecap1en = 1'b0;

    assign afi_arlock =        2'h0;
160
//    assign afi_arcache =       4'h3;
161
    assign afi_arprot =        3'h0;
162
    assign afi_arsize =        2'h3;
163 164 165 166 167
    assign afi_arburst =       2'h1;
    assign afi_arqos =         4'h0;
    assign afi_rdissuecap1en = 1'b0;


Andrey Filippov's avatar
Andrey Filippov committed
168 169
    assign frame_start_chn = start_mclk;
    assign suspend_chn1    = 1'b0;
170 171 172 173 174 175 176 177 178 179
    wire [ 3:0] cmd_a;    // control register address
    wire [31:0] cmd_data; // register data
    wire        cmd_we;   // register write
    
    wire set_ctrl_w;
    wire set_status_w;
    wire set_lo_addr64_w;
    wire set_size64_w;
    wire set_start64_w;
    wire set_len64_w;
180
    wire set_mode_w;
181
    wire set_width64_w;
182 183 184 185 186 187
    reg  [4:0] mode_reg_mclk;
    reg  [4:0] mode_reg;
    wire       cache_debug;
    assign cache_debug=mode_reg[4];
    assign afi_awcache =       mode_reg[3:0]; // 4'h3;
    assign afi_arcache =       mode_reg[3:0]; // 4'h3;
188 189 190 191 192 193 194
    assign set_ctrl_w =         cmd_we && (cmd_a== MEMBRIDGE_CTRL);
    assign set_lo_addr64_w =    cmd_we && (cmd_a== MEMBRIDGE_LO_ADDR64);
    assign set_size64_w =       cmd_we && (cmd_a== MEMBRIDGE_SIZE64);
    assign set_start64_w =      cmd_we && (cmd_a== MEMBRIDGE_START64);
    assign set_len64_w =        cmd_we && (cmd_a== MEMBRIDGE_LEN64);
    assign set_width64_w =      cmd_we && (cmd_a== MEMBRIDGE_WIDTH64);
    assign set_status_w =       cmd_we && (cmd_a== MEMBRIDGE_STATUS_CNTRL);
195
    assign set_mode_w =         cmd_we && (cmd_a== MEMBRIDGE_MODE);
196 197 198 199
    reg [28:0] lo_addr64_mclk;
    reg [28:0] size64_mclk;
    reg [28:0] start64_mclk;
    reg [28:0] len64_mclk;
200 201 202
//  reg [FRAME_WIDTH_BITS+1:0] width64_mclk; // FRAME_WIDTH_BITS in 128 bit bursts
    reg [FRAME_WIDTH_BITS:0] width64_mclk; // FRAME_WIDTH_BITS in 128 bit bursts
    reg [FRAME_WIDTH_BITS:0]   width64_minus1_mclk; // FRAME_WIDTH_BITS in 128 bit bursts
203 204 205
    reg        rdwr_en_mclk;
    reg        rdwr_reset_addr_mclk; // resets system memory address
    reg        start_mclk;
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
`ifdef MEMBRIDGE_DEBUG_READ    
    reg        debug_aw_mclk; // enable sending next address over AFI
    reg        debug_w_mclk; // enable sending next data burst over AFI
    wire       debug_aw; // enable sending next address over AFI, sync to hclk
    wire       debug_w; // enable sending next data burst over AFI, sync to hclk
    reg  [6:0] debug_aw_allowed;
    reg  [8:0] debug_w_allowed;
    reg  [4:0] debug_bufrd_rd;
    reg        debug_disable_set_mclk; // disable debug slowdown
    wire       debug_disable_set;      // disable debug slowdown
    reg        debug_disable; // disable debug slowdown
    wire       debug_aw_ready;
    wire       debug_w_ready;
    assign debug_aw_ready = (!debug_aw_allowed[6] && (|debug_aw_allowed[5:0])) || debug_disable; // > 0
    assign debug_w_ready =  (!debug_w_allowed[8]  && (|debug_w_allowed [7:0]) &&((|debug_w_allowed [7:1]) || !(|debug_bufrd_rd))) || debug_disable; // > 0
`endif    
222 223 224 225 226 227 228
//cmd_wrmem
    always @ (posedge mclk) begin
        if (set_lo_addr64_w)    lo_addr64_mclk       <= {cmd_data[28:4],4'b0}; // align to 16-bursts
        if (set_size64_w)       size64_mclk          <= {cmd_data[28:4],4'b0}; // align to 16-bursts
        if (set_lo_addr64_w)    start64_mclk         <= 0;
        else if (set_start64_w) start64_mclk         <= {cmd_data[28:4],4'b0}; // align to 16-bursts
        if (set_len64_w)        len64_mclk           <= cmd_data[28:0]; // OK not to be aligned
229 230
//        if (set_width64_w)      width64_mclk         <= {~(|cmd_data[FRAME_WIDTH_BITS:0]),cmd_data[FRAME_WIDTH_BITS:0]}; // OK not to be aligned
        if (set_width64_w)      width64_mclk         <= cmd_data[FRAME_WIDTH_BITS:0]; // OK not to be aligned
231
        if (set_ctrl_w)         rdwr_reset_addr_mclk <= cmd_data[1] && !cmd_data[2];
232
        width64_minus1_mclk <= width64_mclk-1;
233 234
    end

Andrey Filippov's avatar
Andrey Filippov committed
235 236
    always @ (posedge mclk) begin
        if      (mrst)       rdwr_en_mclk <= 0;
237 238
        else if (set_ctrl_w) rdwr_en_mclk <= cmd_data[0];
        
Andrey Filippov's avatar
Andrey Filippov committed
239 240
        if   (mrst) start_mclk <= 0;
        else        start_mclk <= set_ctrl_w & cmd_data[1];
241

Andrey Filippov's avatar
Andrey Filippov committed
242
        if      (mrst)       mode_reg_mclk <= 5'h03;
243 244
        else if (set_mode_w) mode_reg_mclk <= cmd_data[4:0];

245
`ifdef MEMBRIDGE_DEBUG_READ
Andrey Filippov's avatar
Andrey Filippov committed
246
        if  (mrst) debug_aw_mclk <= 0;
247 248
        else       debug_aw_mclk <= set_ctrl_w & cmd_data[2];

Andrey Filippov's avatar
Andrey Filippov committed
249
        if  (mrst) debug_w_mclk <= 0;
250 251
        else       debug_w_mclk <= set_ctrl_w & cmd_data[3];

Andrey Filippov's avatar
Andrey Filippov committed
252
        if  (mrst) debug_disable_set_mclk <= 0;
253
        else       debug_disable_set_mclk <= set_ctrl_w & cmd_data[4];
254 255
        
        
256
`endif
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

    end
    
    // syncronize mclk ->hclk
    
    reg [28:0] lo_addr64;
    reg [28:0] size64;
    reg [28:0] start64;
    reg [28:0] len64;
    reg [FRAME_WIDTH_BITS:0]   last_in_line64;
    reg [24:0] last_addr1k; // last adress before rollover w/o 4 LSB
    reg        rdwr_en;     // TODO: Use it?
    reg        rdwr_reset_addr; // resets system memory address
    wire       start_hclk;
    reg        rd_start;
    reg        wr_start;
    reg  [2:0] rdwr_start;
    reg        wr_mode;
    wire       page_ready; // @ posedge hclk
    wire       frame_done; // @ posedge hclk
//next_page_chn
    wire       reset_page_wr; // @ posedge hclk (from @ posedge mclk)
    wire       reset_page_rd; // @ posedge hclk (from @ negedge mclk)
    reg        page_ready_rd;
    reg        page_ready_wr;
    reg        next_page_rd;
    reg        next_page_wr;
    
    wire       next_page; // @ posedge hclk - source 
286
//    wire       busy_next_page; // do not send next_page -previous is crossing clock boundaries
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    
    assign next_page= next_page_wr | next_page_rd;
    
    // incrementing IDs for read (MSB==0) and write (MSB==1)
    reg [4:0] rd_id;
    reg [4:0] wr_id;

    assign afi_arid={1'b1,rd_id};
    assign afi_awid={1'b1,wr_id};
    assign afi_wid= {1'b1,wr_id};

    
    
    
    always @ (posedge hclk) begin
        lo_addr64       <= lo_addr64_mclk;
        size64          <= size64_mclk;
        start64         <= start64_mclk;
        len64           <= len64_mclk;
306
        mode_reg        <= mode_reg_mclk;
307
        last_in_line64  <= width64_minus1_mclk;
308 309 310 311 312
        wr_mode         <= cmd_wrmem;
        rdwr_reset_addr <= rdwr_reset_addr_mclk;
        last_addr1k     <= size64[28:4] - 1;
    end

Andrey Filippov's avatar
Andrey Filippov committed
313 314 315
    always @ (posedge hclk) begin
        if (hrst) rdwr_en <= 0;
        else      rdwr_en <= rdwr_en_mclk;
316 317
        
        
Andrey Filippov's avatar
Andrey Filippov committed
318 319
        if (hrst) rdwr_start <= 0;
        else      rdwr_start <= {rdwr_start[1:0],start_hclk};
320

Andrey Filippov's avatar
Andrey Filippov committed
321 322
        if (hrst) rd_start <= 0;
        else      rd_start <= rdwr_start[2] && !wr_mode; // later to enable adders+ to propagate
323
        
Andrey Filippov's avatar
Andrey Filippov committed
324 325
        if (hrst) wr_start <= 0;
        else      wr_start <= rdwr_start[2] && wr_mode;
326 327 328
        
        page_ready_rd <= page_ready && !wr_mode;
        
Andrey Filippov's avatar
Andrey Filippov committed
329
        if      (hrst)     rd_id <= 0;
330 331
        else if (rd_start) rd_id <= rd_id +1;

Andrey Filippov's avatar
Andrey Filippov committed
332
        if      (hrst)     wr_id <= 0;
333
        else if (wr_start) wr_id <= wr_id +1;
334 335
        
        
336 337 338

    end
    // mclk -> hclk
Andrey Filippov's avatar
Andrey Filippov committed
339 340 341 342
    pulse_cross_clock start_i          (.rst(mrst), .src_clk(mclk), .dst_clk(hclk), .in_pulse(start_mclk), .out_pulse(start_hclk),.busy());
    pulse_cross_clock page_ready_i     (.rst(mrst), .src_clk(mclk), .dst_clk(hclk), .in_pulse(page_ready_chn), .out_pulse(page_ready),.busy());
    pulse_cross_clock frame_done_i     (.rst(mrst), .src_clk(mclk), .dst_clk(hclk), .in_pulse(frame_done_chn), .out_pulse(frame_done),.busy());
    pulse_cross_clock  reset_page_wr_i (.rst(mrst), .src_clk(mclk), .dst_clk(hclk), .in_pulse(xfer_reset_page_wr), .out_pulse(reset_page_wr),.busy());
343 344 345

`ifdef MEMBRIDGE_DEBUG_READ
    // mclk -> hclk, debug-only
Andrey Filippov's avatar
Andrey Filippov committed
346 347 348
    pulse_cross_clock debug_aw_i         (.rst(hrst), .src_clk(mclk), .dst_clk(hclk), .in_pulse(debug_aw_mclk), .out_pulse(debug_aw),.busy());
    pulse_cross_clock debug_w_i          (.rst(hrst), .src_clk(mclk), .dst_clk(hclk), .in_pulse(debug_w_mclk),  .out_pulse(debug_w), .busy());
    pulse_cross_clock debug_disable_set_i(.rst(hrst),.src_clk(mclk),.dst_clk(hclk), .in_pulse(debug_disable_set_mclk),.out_pulse(debug_disable_set), .busy());
349
`endif    
350
    // negedge mclk -> hclk (verify clock inversion is absorbed)
Andrey Filippov's avatar
Andrey Filippov committed
351 352 353
    reg mrstn = 1;
    always @ (negedge mclk) mrstn <= mrst;
    pulse_cross_clock  reset_page_rd_i (.rst(mrstn), .src_clk(~mclk),.dst_clk(hclk), .in_pulse(xfer_reset_page_rd), .out_pulse(reset_page_rd),.busy());
354 355
    
    // hclk -> mclk
356 357 358 359 360 361 362 363 364 365 366 367 368 369
//    pulse_cross_clock next_page_i  (.rst(hrst), .src_clk(hclk), .dst_clk(mclk), .in_pulse(next_page), .out_pulse(next_page_chn),.busy(busy_next_page));
    
    elastic_cross_clock #(
        .WIDTH(2),
        .EXTRA_DLY(0)
    ) elastic_cross_clock_i (
        .rst          (hrst),          // input
        .src_clk      (hclk),          // input
        .dst_clk      (mclk),          // input
        .in_pulses    (next_page),     // input
        .out_pulse    (next_page_chn), // output
        .busy         ()               // output
    );
    
370 371
    
    // Common to both directions
Andrey Filippov's avatar
Andrey Filippov committed
372
    localparam DELAY_ADVANCE_ADDR=3;    
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    reg [28:0] rel_addr64; // realtive (to lo_addr) address
    wire                         advance_rel_addr_w;
    wire                         advance_rel_addr_wr;
    wire                         advance_rel_addr_rd;
    
    reg                          advance_rel_addr;

    reg [DELAY_ADVANCE_ADDR-1:0] advance_rel_addr_d;
    reg                   [28:0] left64;
    reg                          last_burst;
    reg                          rollover;
    reg                   [ 3:0] afi_len;
    reg                   [ 4:0] afi_len_plus1;
    reg                          low4_zero;
    
    reg [28:0]                 buf_left64;      // number of 64-bit words yet to be read from the DDR3 (or written to it)
    reg [FRAME_WIDTH_BITS:0]   buf_in_line64;   // number of last 64-bit words in line
    
    //last_addr1k
    assign afi_awlen = afi_len;
    assign afi_arlen = afi_len;
    reg [28:0] axi_addr64;
    wire left_zero;
    
    assign afi_awaddr={axi_addr64,3'b0};
    assign afi_araddr={axi_addr64,3'b0};
    
    assign left_zero = low4_zero && last_burst;
Andrey Filippov's avatar
Andrey Filippov committed
401 402 403
    always @ (posedge hclk) begin
        if (hrst) advance_rel_addr_d <= 0;
        else      advance_rel_addr_d <= {advance_rel_addr_d[DELAY_ADVANCE_ADDR-2:0],advance_rel_addr};
404 405 406 407 408 409 410 411
                
    end

    reg        read_started;
    reg        write_busy;
    wire       rw_in_progress;
    reg        busy;
    reg        done;
412
    reg        pre_done;
413 414 415 416

    assign rw_in_progress = read_started || write_busy;
    
    always @ (posedge hclk) begin
Andrey Filippov's avatar
Andrey Filippov committed
417
        advance_rel_addr <= advance_rel_addr_w && !advance_rel_addr && !(|advance_rel_addr_d); // make sure advance_rel_addr_w is recalculated after address change
418 419 420 421
        last_burst <= ! (|left64[28:4]);
        rollover <= rel_addr64[28:4] == last_addr1k;
        low4_zero <= ! (|left64[3:0]);
        if (rdwr_start[0] && rdwr_reset_addr) rel_addr64 <= start64;
Andrey Filippov's avatar
Andrey Filippov committed
422
        else if (advance_rel_addr) rel_addr64 <= last_burst?(rel_addr64 + {25'h0,left64[3:0]}) : (rollover?29'h0:(rel_addr64 + 29'h10));
423 424 425 426

        axi_addr64 <= lo_addr64 + rel_addr64;
        
        if (rdwr_start)            left64 <= len64;
Andrey Filippov's avatar
Andrey Filippov committed
427
        else if (advance_rel_addr) left64 <= last_burst? 0: (left64 - 29'h10);
428 429 430 431 432 433 434 435 436 437 438 439 440
        
        afi_len       <= (|left64[28:4])?4'hf : (left64[3:0]-1);
        afi_len_plus1 <= (|left64[28:4]) ? 5'h10 : {1'b0,left64[3:0]};
        
        page_ready_rd <= page_ready && !wr_mode;
        page_ready_wr <= page_ready &&  wr_mode;
        
        if (!rw_in_progress) buf_left64 <= len64;
        else if (buf_rdwr)   buf_left64 <= buf_left64 - 1;
        
        if (!rw_in_progress) buf_in_line64 <= 0;
        else if (buf_rdwr)   buf_in_line64 <= is_last_in_line? {(FRAME_WIDTH_BITS+1){1'b0}} : (buf_in_line64 +1);
        
441 442
        if (hrst) next_page_rd <= 0;
        else      next_page_rd <= next_page_rd_w;
443
        
444 445
        if (hrst) next_page_wr <= 0;
        else      next_page_wr <= next_page_wr_w;
446 447 448 449 450
        
    end

    // DDR3 read - AFI write
    //rdwr_en    
451 452
    reg  [7:0] axi_arw_requested;     // 64-bit words to be read/written over axi queued to AR/AW channels
    reg  [7:0] axi_bursts_requested;  // number of bursts requested
453 454 455 456 457 458
    reg  [7:0] wresp_conf;         // number of 64-bit words confirmed through axi b channel
    wire [7:0] axi_wr_pending;     // Number of words qued to AW but not yet confirmed through B-channel;
    wire [7:0] axi_rd_pending;

    reg  [7:0] axi_rd_received;
    assign axi_rd_pending= axi_arw_requested - axi_rd_received;
459 460
//    assign axi_wr_pending= axi_arw_requested - wresp_conf;
    assign axi_wr_pending= axi_bursts_requested - wresp_conf;
461
    
462 463 464 465 466 467 468 469 470 471 472 473 474
    reg        read_busy;
    reg        read_over;
    reg        afi_bvalid_r;
    reg  [1:0] read_page;
//    reg  [6:0] read_addr;
    reg  [2:0] read_pages_ready;
    
    assign afi_bready = 1'b1; // always ready to receive confirmation
    
    reg        afi_wd_safe_not_full;
    reg        afi_wa_safe_not_full;
    
    assign advance_rel_addr_w =  advance_rel_addr_wr || advance_rel_addr_rd;
475 476 477 478 479 480
//    assign advance_rel_addr_wr = read_started && afi_wa_safe_not_full && (|left64); // left 64 is decremented by 16, except possibly the last (partial)
`ifdef MEMBRIDGE_DEBUG_READ
    assign advance_rel_addr_wr = read_started && afi_wa_safe_not_full && (|left64) && debug_aw_ready; // debugging ddr3 -> system
`else
    assign advance_rel_addr_wr = read_started && afi_wa_safe_not_full && (|left64);
`endif    
481 482
    assign afi_awvalid=advance_rel_addr && read_started;
    
Andrey Filippov's avatar
Andrey Filippov committed
483 484
    always @ (posedge hclk) begin
        if      (hrst)      read_busy <= 0;
485 486
        else if (rd_start)  read_busy <= 1;
        else if (read_over) read_busy <= 0;
487 488 489
        
        
        
Andrey Filippov's avatar
Andrey Filippov committed
490
        if      (hrst)       read_started <= 0;
491
        else if (!read_busy) read_started <= 0;
492
        else if (wr_mode)    read_started <= 0; // just debugging, making sure read is disabled in write mode
493
        else if (page_ready) read_started <= 1; // first page is in the buffer - use it to mask page number comparison
494 495
        
`ifdef MEMBRIDGE_DEBUG_READ
Andrey Filippov's avatar
Andrey Filippov committed
496
        if      (hrst)                      debug_aw_allowed <= 0;
497 498 499 500 501
        else if (!read_busy)                debug_aw_allowed <= 0;
        else if ( debug_aw && !afi_awvalid) debug_aw_allowed <= debug_aw_allowed + 1;
        else if (!debug_aw &&  afi_awvalid) debug_aw_allowed <= debug_aw_allowed - 1;
        
        
Andrey Filippov's avatar
Andrey Filippov committed
502
        if      (hrst)                                   debug_w_allowed <= 0;
503 504 505 506
        else if (!read_busy)                             debug_w_allowed <= 0;
        else if ( debug_w && !(afi_wvalid && afi_wlast)) debug_w_allowed <= debug_w_allowed + 1;
        else if (!debug_w &&  (afi_wvalid && afi_wlast)) debug_w_allowed <= debug_w_allowed - 1;
        
Andrey Filippov's avatar
Andrey Filippov committed
507
        if      (hrst)              debug_disable <= 0;
508 509 510 511
        else if (!read_busy)        debug_disable <= 0;
        else if (debug_disable_set) debug_disable <= 1;
`endif        
        
512 513

        afi_bvalid_r <=afi_bvalid;
514
        
Andrey Filippov's avatar
Andrey Filippov committed
515
        if      (hrst)         wresp_conf <= 0;
516 517
        else if (!read_busy)   wresp_conf <= 0;
        else if (afi_bvalid_r) wresp_conf <= wresp_conf +1;
Andrey Filippov's avatar
Andrey Filippov committed
518
        
519
        read_over <= left_zero && (axi_wr_pending == 0) && read_started; 
520
        
Andrey Filippov's avatar
Andrey Filippov committed
521
        if      (hrst)           read_page <= 0;
522 523 524
        else if (reset_page_rd)  read_page <= 0;
        else if (next_page_rd_w) read_page <= read_page + 1;

Andrey Filippov's avatar
Andrey Filippov committed
525
        if      (hrst)                              read_pages_ready <= 0;
526 527 528 529
        else if (!read_busy)                        read_pages_ready <= 0;
        else if ( page_ready_rd && !next_page_rd_w) read_pages_ready <= read_pages_ready +1;
        else if (!page_ready_rd &&  next_page_rd_w) read_pages_ready <= read_pages_ready -1;
        
Andrey Filippov's avatar
Andrey Filippov committed
530 531
        if (hrst) afi_wd_safe_not_full <= 0;
        else      afi_wd_safe_not_full <=  rdwr_en && (!afi_wcount[7] && !(&afi_wcount[6:3]));
532

Andrey Filippov's avatar
Andrey Filippov committed
533 534
        if (hrst) afi_wa_safe_not_full <= 0;
        else      afi_wa_safe_not_full <=  rdwr_en && (!afi_wacount[5] && !(&afi_wacount[4:2]));
535
        
Andrey Filippov's avatar
Andrey Filippov committed
536 537
        if (hrst) busy <= 0;
        else      busy <=  read_busy || write_busy;
538
        
Andrey Filippov's avatar
Andrey Filippov committed
539
        if (hrst) pre_done <= 0; // delay done to turn on same time busy is off
540 541
        else     pre_done <= (write_busy && frame_done) || (read_busy && read_over);
        
Andrey Filippov's avatar
Andrey Filippov committed
542
        if      (hrst)           done <= 0;
543 544 545
        else if (!rdwr_en)       done <= 0; // disabling when idle will reset done
        else if (pre_done)       done <= 1;
        else if (rdwr_start)     done <= 0;
546 547 548 549 550 551 552 553
        
    end
    
    // handle interaction with the buffer, advance addresses, keep track of partial (last) pages in each line
    wire                       bufrd_rd_w;
    wire                       bufwr_we_w; // TODO: assign
    
    reg                  [2:0] bufrd_rd;
554
    reg   [BUFWR_WE_WIDTH-1:0] bufwr_we;
555 556 557 558 559
    reg                        buf_rdwr; // equiv to  bufrd_rd[0] || bufwr_we)
    wire                       is_last_in_line;
    wire                       is_last_in_page;
    wire                       next_page_rd_w;
    wire                       next_page_wr_w;
560 561
//    assign next_page_rd_w = read_started && !busy_next_page && is_last_in_page && bufrd_rd[0];
    assign next_page_rd_w = read_started && is_last_in_page && bufrd_rd[0];
562 563
    assign is_last_in_line = buf_in_line64 == last_in_line64;
    assign is_last_in_page = is_last_in_line || (&buf_in_line64[6:0]);
564 565 566
`ifdef MEMBRIDGE_DEBUG_READ
    assign bufrd_rd_w = afi_wd_safe_not_full && (|read_pages_ready[2:1] || (read_pages_ready[0] && !is_last_in_page)) && debug_w_ready;
`else
567
    assign bufrd_rd_w = afi_wd_safe_not_full && (|read_pages_ready[2:1] || (read_pages_ready[0] && !is_last_in_page));
568
`endif    
569
//last_in_line64 - last word number in scan line
570
    reg                        left_was_1; // was <=1 (0 does not matter)  valid next after buffer address
571
    reg                  [3:0] src_wcntr;
572 573
//    reg                  [2:0] wlast_in_burst;
    reg                        wlast; // valid 2 after buffer address, same as wvalid
574
    
575 576 577 578 579
    reg                        src_was_f;  // valid next after buffer address

//    assign afi_wlast = wlast_in_burst[2];
    assign afi_wlast = wlast;

580 581 582 583 584 585 586
    always @ (posedge hclk) begin
        if (!rw_in_progress) left_was_1 <= 0;
        else if   (buf_rdwr) left_was_1 <= !(|buf_left64[28:1]);
        
        if    (!read_started) src_wcntr <= 0;
        else if (bufrd_rd[0]) src_wcntr <= src_wcntr+1;
        
587 588 589 590 591 592 593 594 595
        if    (!read_started) src_was_f <= 0;          
        else if (bufrd_rd[0]) src_was_f <= &src_wcntr; // valid with buffer address
        
//        if    (!read_started) wlast_in_burst <= 0;
//        else if (bufrd_rd[0]) wlast_in_burst <= {wlast_in_burst[1:0],left_was_1 | (&src_wcntr)};

        if    (!read_started) wlast <= 0;
        else if (bufrd_rd[1]) wlast <= left_was_1 || src_was_f;
        
596 597
        
        bufrd_rd <= {bufrd_rd[1:0], bufrd_rd_w };
598 599 600
`ifdef MEMBRIDGE_DEBUG_READ
        debug_bufrd_rd<= {debug_bufrd_rd[3:0], bufrd_rd_w };
`endif        
601
        buf_rdwr <= bufrd_rd_w || bufwr_we_w;
602
        bufwr_we <= {bufwr_we[BUFWR_WE_WIDTH-2:0],bufwr_we_w};
603 604 605 606 607 608 609 610 611 612 613 614
        
    end
    assign afi_wvalid=bufrd_rd[2];

    // write to ddr3 from afi
    reg        afi_rd_safe_not_empty;
    reg        afi_ra_safe_not_full;
    reg        afi_safe_rd_pending;
 //    assign advance_rel_addr_wr = read_started && afi_wa_safe_not_full && (|left64); // left 64 is decremented by 16, except possibly the last (partial)
    assign advance_rel_addr_rd = write_busy && afi_ra_safe_not_full && afi_safe_rd_pending && (|left64);
    assign afi_arvalid=advance_rel_addr && write_busy;

615 616
//    assign next_page_wr_w = write_busy && !busy_next_page && is_last_in_page && bufwr_we[0];
    assign next_page_wr_w = write_busy && is_last_in_page && bufwr_we[0];
617 618 619 620 621 622 623 624 625 626 627
    
    assign bufwr_we_w= afi_rd_safe_not_empty && !write_pages_ready[2] && (!(&write_pages_ready[1:0]) ||  !is_last_in_page);
    
    // handle buffer address, page 
    reg  [1:0] write_page;        // current number of buffer page
    reg  [2:0] write_pages_ready; // number of pages in the buffer
    reg  [1:0] write_page_r;      // 1-cycle delayed page address
    reg  [6:0] buf_in_line64_r;   // 1-cycle delayed buffer address
    
    assign afi_rready = bufwr_we[0];

Andrey Filippov's avatar
Andrey Filippov committed
628 629
    always @ (posedge hclk) begin
        if      (hrst)       write_busy <= 0;
630
        else if (wr_start)   write_busy <= 1;
631
        else if (!wr_mode)   write_busy <= 0; // Just debugging, making sure write mode is disabled in read mode
632 633
        else if (frame_done) write_busy <= 0;

Andrey Filippov's avatar
Andrey Filippov committed
634
        if      (hrst)                          axi_arw_requested <= 0;
635 636
        else if (!write_busy && !read_started)  axi_arw_requested <= 0;
        else if (advance_rel_addr)              axi_arw_requested <= axi_arw_requested + afi_len_plus1;
637
        
Andrey Filippov's avatar
Andrey Filippov committed
638
        if      (hrst)                          axi_bursts_requested <= 0;
639 640
        else if (!write_busy && !read_started)  axi_bursts_requested <= 0;
        else if (advance_rel_addr)              axi_bursts_requested <= axi_bursts_requested + 1;
641

Andrey Filippov's avatar
Andrey Filippov committed
642
        if      (hrst)             axi_rd_received <= 0;
643 644 645 646
        else if (!write_busy)      axi_rd_received <= 0;
        else if (bufwr_we[0])      axi_rd_received <= axi_rd_received + 1;
        
        
Andrey Filippov's avatar
Andrey Filippov committed
647
        if (hrst) afi_rd_safe_not_empty <= 0;
648 649
         // allow 1 cycle latency, no continuous reads when FIFO is low (like in the very end of the transfer)
         // Adjust '2' in afi_rcount[6:2] ? 
650
        else     afi_rd_safe_not_empty <= rdwr_en && ( afi_rcount[7] || (|afi_rcount[6:SAFE_RD_BITS]) || (!(|bufwr_we) && !bufwr_we_w && afi_rvalid));
651

Andrey Filippov's avatar
Andrey Filippov committed
652 653
        if (hrst) afi_ra_safe_not_full <= 0;
        else      afi_ra_safe_not_full <= rdwr_en && ( !afi_racount[2] && !(&afi_racount[1:0]));
654
        
Andrey Filippov's avatar
Andrey Filippov committed
655
        if      (hrst)        afi_safe_rd_pending <= 0;
656 657
        else if (!write_busy) afi_safe_rd_pending <= 0;
        else                  afi_safe_rd_pending <= rdwr_en && ( !axi_rd_pending[7] && !(&axi_rd_pending[6:4]));
658 659
        
        // handle buffer address, page 
Andrey Filippov's avatar
Andrey Filippov committed
660
        if      (hrst)           write_page <= 0;
661 662 663
        else if (reset_page_wr)  write_page <= 0;
        else if (next_page_wr_w) write_page <= write_page + 1;

Andrey Filippov's avatar
Andrey Filippov committed
664
        if      (hrst)                              write_pages_ready <= 0;
Andrey Filippov's avatar
Andrey Filippov committed
665
        else if (!write_busy)                       write_pages_ready <= 0;
666 667 668 669
        else if ( page_ready_wr && !next_page_wr_w) write_pages_ready <= write_pages_ready -1; //+1;
        else if (!page_ready_wr &&  next_page_wr_w) write_pages_ready <= write_pages_ready +1; //-1;

    end
670 671 672 673 674 675 676 677
`ifdef MEMBRIDGE_DEBUG_WRITE
    reg [30:0] dbg_read_counter;
    always @ (posedge hclk) begin
        if     (!write_busy )  dbg_read_counter <= 0;
        else if (bufwr_we[0])  dbg_read_counter <= dbg_read_counter + 1;
    end
`endif    
    
678
    reg [63:0] rdata_r;        
679 680 681
    always @ (posedge hclk) begin
        write_page_r    <= write_page;
        buf_in_line64_r <= buf_in_line64[6:0];
682
//        rdata_r <= afi_rdata;
683 684 685
`ifdef MEMBRIDGE_DEBUG_WRITE
        rdata_r <= cache_debug?{dbg_read_counter,1'h1,dbg_read_counter,1'h0}:afi_rdata[63:0]; // debugging
`else        
686
        rdata_r <= cache_debug?{wr_id[3:0],2'b0,write_page_r[1:0],afi_rcount[7:0],afi_rdata[47:0]}:afi_rdata[63:0]; // debugging
687
`endif        
688 689 690 691 692 693 694 695 696
    end    
        
    cmd_deser #(
        .ADDR       (MEMBRIDGE_ADDR),
        .ADDR_MASK  (MEMBRIDGE_MASK),
        .NUM_CYCLES (6),
        .ADDR_WIDTH (4),
        .DATA_WIDTH (32)
    ) cmd_deser_32bit_i (
Andrey Filippov's avatar
Andrey Filippov committed
697 698 699 700 701 702 703 704
        .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[3:0] 
        .data       (cmd_data),  // output[31:0] 
        .we         (cmd_we)     // output
705 706 707 708
    );

    status_generate #(
        .STATUS_REG_ADDR  (MEMBRIDGE_STATUS_REG),
709 710 711
`ifdef MEMBRIDGE_DEBUG_READ
        .PAYLOAD_BITS     (18) // 2) // With debug
`else
712
        .PAYLOAD_BITS     (18) //2)
713
`endif        
714
    ) status_generate_i (
Andrey Filippov's avatar
Andrey Filippov committed
715 716 717 718 719
        .rst              (1'b0),                                            //   rst), // input
        .clk              (mclk),                                            // input
        .srst             (mrst),                                            // input
        .we               (set_status_w),                                    // input
        .wd               (cmd_data[7:0]),                                   // input[7:0]
720 721 722
`ifdef MEMBRIDGE_DEBUG_READ
        .status           ({debug_aw_allowed, debug_w_allowed, done, busy}), // input[25:0]
`else
Andrey Filippov's avatar
Andrey Filippov committed
723
        .status           ({axi_arw_requested, wresp_conf, done, busy}),     // input[25:0] 
724
`endif
Andrey Filippov's avatar
Andrey Filippov committed
725 726 727
        .ad               (status_ad),                                       // output[7:0] 
        .rq               (status_rq),                                       // output
        .start            (status_start)                                     // input
728 729 730
    );

// Port 1rd (read DDR to AFI) buffer, linear
731 732 733 734 735 736 737 738 739 740 741 742 743
wire [63:0] afi_wdata0;
`ifdef MEMBRIDGE_DEBUG_WRITE
    reg [15:0] dbg_write_counter;
    always @ (posedge hclk) begin
        if (!read_busy || !cache_debug) dbg_write_counter <= 0;
        else if (bufrd_rd[1])          dbg_write_counter <= dbg_write_counter + 1;
    end
    assign afi_wdata = {afi_wdata0[63:16], dbg_write_counter[0]? dbg_write_counter[15:0]: afi_wdata0[15:0]};
`else
    assign afi_wdata = afi_wdata0;
`endif


744 745 746
    mcntrl_buf_rd #(
        .LOG2WIDTH_RD(6) // 64 bit external interface
    ) chn1rd_buf_i (
Andrey Filippov's avatar
Andrey Filippov committed
747
        .ext_clk      (hclk),                           // input
748
        .ext_raddr    ({read_page,buf_in_line64[6:0]}), // input[8:0] 
Andrey Filippov's avatar
Andrey Filippov committed
749 750
        .ext_rd       (bufrd_rd[0]),                    // input
        .ext_regen    (bufrd_rd[1]),                    // input
751 752
        .ext_data_out (afi_wdata0),                     // output[63:0]
//        .emul64       (1'b0),                           // input Modify buffer addresses (used for JP4 until a 64-wide mode is implemented)
Andrey Filippov's avatar
Andrey Filippov committed
753 754 755 756 757 758 759
        .wclk         (!mclk),                          // input
        .wpage_in     (2'b0),                           // input[1:0] 
        .wpage_set    (xfer_reset_page_rd),             // input  TODO: Generate @ negedge mclk on frame start
        .page_next    (buf_wpage_nxt),                  // input
        .page         (),                               // output[1:0]
        .we           (buf_wr),                         // input
        .data_in      (buf_wdata)                       // input[63:0] 
760 761 762 763 764 765
    );

// Port 1wr (write DDR from AFI) buffer, linear
    mcntrl_buf_wr #(
         .LOG2WIDTH_WR(6)  // 64 bit external interface
    ) chn1wr_buf_i (
Andrey Filippov's avatar
Andrey Filippov committed
766
        .ext_clk      (hclk),                                 // input
767
        .ext_waddr    ({write_page_r, buf_in_line64_r[6:0]}), // input[8:0] 
Andrey Filippov's avatar
Andrey Filippov committed
768 769 770 771 772 773 774 775 776
        .ext_we       (bufwr_we[1]),                          // input
        .ext_data_in  (rdata_r),                              //afi_rdata), // input[63:0] buf_wdata - from AXI
        .rclk         (mclk),                                 // input
        .rpage_in     (2'b0),                                 // input[1:0] 
        .rpage_set    (xfer_reset_page_wr),                   // input  @ posedge mclk
        .page_next    (buf_rpage_nxt),                        // input
        .page         (),                                     // output[1:0]
        .rd           (buf_rd),                               // input
        .data_out     (buf_rdata)                             // output[63:0] 
777 778
    );

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
`ifdef DEBUG_RING
    debug_slave #(
        .SHIFT_WIDTH       (32),
        .READ_WIDTH        (32),
        .WRITE_WIDTH       (32),
        .DEBUG_CMD_LATENCY (DEBUG_CMD_LATENCY)
    ) debug_slave_i (
        .mclk       (mclk),          // input
        .mrst       (mrst),          // input
        .debug_di   (debug_di), // input
        .debug_sl   (debug_sl),      // input
        .debug_do   (debug_do), // output
        .rd_data   ({
        5'b0, afi_racount[2:0],
        afi_rcount[7:0],
        2'b0, afi_wacount[5:0],
        afi_wcount[7:0]
        }), // input[31:0]
        .wr_data    (), // output[31:0]  - not used
        .stb        () // output  - not used
    );
`endif    

802 803
endmodule