From 0b384c661973d625071f126469365ca89f9bb74b Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 20 Aug 2024 19:29:09 -0700 Subject: [PATCH] sim: implementation of a simulation model DP16KD Not fully verified with tests yet, WIP --- flake.nix | 1 + sim/DP16KD.v | 798 ++++++++++++++++++ ..._DP16KD_18b_sync_nowriteout_unregistered.v | 106 +++ 3 files changed, 905 insertions(+) create mode 100644 sim/DP16KD.v create mode 100644 sim/tb/tb_DP16KD_18b_sync_nowriteout_unregistered.v diff --git a/flake.nix b/flake.nix index 6be94bb..3100b9d 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,7 @@ yosys z3 kgraphviewer + iverilog ]; }; }); diff --git a/sim/DP16KD.v b/sim/DP16KD.v new file mode 100644 index 0000000..0a14f2e --- /dev/null +++ b/sim/DP16KD.v @@ -0,0 +1,798 @@ +// DP16KD__INTERNAL is a dual-port memory with equal width ports and +// read-before-write behavior (writes output the previous value at the +// write address). +module DP16KD__INTERNAL#( + parameter DATA_WIDTH = 18, + parameter ADDR_WIDTH = 10, + parameter RESETMODE = "SYNC", + parameter WRITEMODE_A = "NORMAL", + parameter WRITEMODE_B = "NORMAL" +) ( + input CLKA, + input RSTA, + input CEA, + input WEA, + input [ADDR_WIDTH-1:0] ADA, + input [DATA_WIDTH-1:0] DIA, + output reg [DATA_WIDTH-1:0] DOA, + + input CLKB, + input RSTB, + input CEB, + input WEB, + input [ADDR_WIDTH-1:0] ADB, + input [DATA_WIDTH-1:0] DIB, + output reg [DATA_WIDTH-1:0] DOB); + + (* no_rw_check *) + reg [DATA_WIDTH-1:0] ram[(2**ADDR_WIDTH)-1:0]; + + wire [DATA_WIDTH-1:0] undef = {DATA_WIDTH{1'bx}}; + + if (RESETMODE == "SYNC") begin : sync_ram + always @(posedge CLKA) begin + if (RSTA) + DOA <= {DATA_WIDTH {1'b0}}; + else if (CEA) begin + if (WEA) begin + ram[ADA] <= DIA; + case (WRITEMODE_A) + "NORMAL": DOA <= undef; + "WRITETHROUGH": DOA <= DIA; + "READBEFOREWRITE": DOA <= ram[ADA]; + endcase + end + else + DOA <= ram[ADA]; + end + end + + always @(posedge CLKB) begin + if (RSTB) + DOB <= {DATA_WIDTH {1'b0}}; + else if (CEB) begin + if (WEB) begin + ram[ADB] <= DIB; + case (WRITEMODE_B) + "NORMAL": DOB <= undef; + "WRITETHROUGH": DOB <= DIB; + "READBEFOREWRITE": DOB <= ram[ADB]; + endcase + end + else + DOB <= ram[ADB]; + end + end + end // if (RESETMODE == "SYNC") + else begin : async_ram + always @(posedge CLKA, posedge RSTA) begin + if (RSTA) + DOA <= {DATA_WIDTH {1'b0}}; + else if (CEA) begin + if (WEA) begin + ram[ADA] <= DIA; + case (WRITEMODE_A) + "NORMAL": DOA <= undef; + "WRITETHROUGH": DOA <= DIA; + "READBEFOREWRITE": DOA <= ram[ADA]; + endcase + end + else + DOA <= ram[ADA]; + end + end + + always @(posedge CLKB, posedge RSTB) begin + if (RSTB) + DOB <= {DATA_WIDTH {1'b0}}; + else if (CEB) begin + if (WEB) begin + ram[ADB] <= DIB; + case (WRITEMODE_B) + "NORMAL": DOB <= undef; + "WRITETHROUGH": DOB <= DIB; + "READBEFOREWRITE": DOB <= ram[ADB]; + endcase + end + else + DOB <= ram[ADB]; + end + end + end // else: !if(RESETMODE == "SYNC") + +endmodule + +// DP16KD__SPLIT_MUL is a dual-port memory where port B's width is a +// multiple of port A (e.g. 2b and 4b, or 9b and 18b). The larger port +// effectively accesses multiple words from the smaller port in a +// single operation. +module DP16KD__SPLIT_MUL#( + parameter DATA_WIDTH_A = 1, + parameter ADDR_WIDTH_A = 14, + parameter DATA_WIDTH_B = 4, + parameter RESETMODE = "SYNC", + parameter WRITEMODE_A = "NORMAL", + parameter WRITEMODE_B = "NORMAL" +)( + input CLKA, + input RSTA, + input CEA, + input WEA, + input [ADDR_WIDTH_A-1:0] ADA, + input [DATA_WIDTH_A-1:0] DIA, + output [DATA_WIDTH_A-1:0] DOA, + + input CLKB, + input RSTB, + input CEB, + input WEB, + input [ADDR_WIDTH_B-1:0] ADB, + input [DATA_WIDTH_B-1:0] DIB, + output [DATA_WIDTH_B-1:0] DOB +); + localparam ADDR_WIDTH_B = $clog2((DATA_WIDTH_A * (2**ADDR_WIDTH_A)) / DATA_WIDTH_B); + localparam CSA_WIDTH = ADDR_WIDTH_A - ADDR_WIDTH_B; + localparam NUM_MEMS = 2**CSA_WIDTH; + + // Convert the lower bits of ADA into a one-hot signal to select one + // of the sub-memories. This stripes consecutive addresses across + // all sub-memories, which the wider port can then access in + // parallel and get the correct view of bits. + wire [CSA_WIDTH-1:0] CSA = ADA[CSA_WIDTH-1:0]; + wire [NUM_MEMS-1:0] CSA_onehot = {NUM_MEMS { CEA }} & 1 << CSA; + + wire [(DATA_WIDTH_A*NUM_MEMS)-1:0] DOA_SUB; + assign DOA = DOA_SUB[CSA*DATA_WIDTH_A +: DATA_WIDTH_A]; + + DP16KD__INTERNAL#(.DATA_WIDTH(DATA_WIDTH_A), + .ADDR_WIDTH(ADDR_WIDTH_B), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) rams [NUM_MEMS-1:0] + (.CLKA(CLKA), .RSTA(RSTA), .CEA(CSA_onehot), .WEA(WEA), + .ADA(ADA[ADDR_WIDTH_A-1:CSA_WIDTH]), .DIA(DIA), .DOA(DOA_SUB), + + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), + .ADB(ADB), .DIB(DIB), .DOB(DOB)); +endmodule // DP16KD__SPLIT_MUL + +// DP16KD__DECOMPOSE is a dual-port memory where the port widths are +// 1b, 2b, 4b, 9b or 18b, and port A's width is less than or equal to +// port B's width. The memory is implemented by mapping the two ports +// onto an internal memory with the same mapping as the ECP5 DP16KD: +// power of two words get packed into each other, 9b words get packed +// into 18b words, and a memory with a single 9b or 18b port gets +// treated like an 8b or 16b port, with the remaining ECC bits kept +// separate and invisible to the power-of-two port. +module DP16KD__DECOMPOSE#( + parameter RESETMODE = "SYNC", + parameter DATA_WIDTH_A = 18, + parameter DATA_WIDTH_B = 18, + parameter WRITEMODE_A = "NORMAL", + parameter WRITEMODE_B = "NORMAL" +)( + input CLKA, + input RSTA, + input CEA, + input WEA, + input [ADDR_WIDTH_A-1:0] ADA, + input [DATA_WIDTH_A-1:0] DIA, + output [DATA_WIDTH_A-1:0] DOA, + + input CLKB, + input RSTB, + input CEB, + input WEB, + input [ADDR_WIDTH_B-1:0] ADB, + input [DATA_WIDTH_B-1:0] DIB, + output [DATA_WIDTH_B-1:0] DOB +); + localparam ADDR_WIDTH_A = (DATA_WIDTH_A == 18 ? 10 : + DATA_WIDTH_A == 9 ? 11 : + DATA_WIDTH_A == 4 ? 12 : + DATA_WIDTH_A == 2 ? 13 : + DATA_WIDTH_A == 1 ? 14 : -1); + localparam ADDR_WIDTH_B = (DATA_WIDTH_B == 18 ? 10 : + DATA_WIDTH_B == 9 ? 11 : + DATA_WIDTH_B == 4 ? 12 : + DATA_WIDTH_B == 2 ? 13 : + DATA_WIDTH_B == 1 ? 14 : -1); + localparam DATA_WIDTH_HYBRID = (DATA_WIDTH_A < 9 && DATA_WIDTH_B >= 9) || (DATA_WIDTH_A >= 9 && DATA_WIDTH_B < 9); + + if (DATA_WIDTH_A == DATA_WIDTH_B) begin : equal_width_ports + DP16KD__INTERNAL#(.DATA_WIDTH(DATA_WIDTH_A), + .ADDR_WIDTH(ADDR_WIDTH_A), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ram + (.CLKA(CLKA), .RSTA(RSTA), .CEA(CEA), .WEA(WEA), .ADA(ADA), .DIA(DIA), .DOA(DOA), + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), .ADB(ADB), .DIB(DIB), .DOB(DOB)); + end + else if (DATA_WIDTH_HYBRID) begin : one_ecc_port + if (DATA_WIDTH_B == 9) begin : one_ecc_bit + DP16KD__INTERNAL#(.DATA_WIDTH(1), + .ADDR_WIDTH(ADDR_WIDTH_B), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ecc + (.CLKA(1'b0), .RSTA(1'b0), .CEA(1'b0), .WEA(1'b0), .ADA({ADDR_WIDTH_B {1'b0}}), .DIA(1'b0), .DOA(), + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), .ADB(ADB), .DIB(DIB[8]), .DOB(DOB[8])); + DP16KD__SPLIT_MUL#(.DATA_WIDTH_A(DATA_WIDTH_A), + .ADDR_WIDTH_A(ADDR_WIDTH_A), + .DATA_WIDTH_B(DATA_WIDTH_B-1), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ram + (.CLKA(CLKA), .RSTA(RSTA), .CEA(CEA), .WEA(WEA), .ADA(ADA), .DIA(DIA), .DOA(DOA), + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), .ADB(ADB), .DIB(DIB[7:0]), .DOB(DOB[7:0])); + end // if (DATA_WIDTH_B == 9) + else begin : two_ecc_bits + DP16KD__INTERNAL#(.DATA_WIDTH(2), + .ADDR_WIDTH(ADDR_WIDTH_B), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ecc + (.CLKA(1'b0), .RSTA(1'b0), .CEA(1'b0), .WEA(1'b0), .ADA({ADDR_WIDTH_B {1'b0}}), .DIA(1'b0), .DOA(), + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), .ADB(ADB), .DIB({DIB[17], DIB[8]}), .DOB({DOB[17], DOB[8]})); + DP16KD__SPLIT_MUL#(.DATA_WIDTH_A(DATA_WIDTH_A), + .ADDR_WIDTH_A(ADDR_WIDTH_A), + .DATA_WIDTH_B(DATA_WIDTH_B-2), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ram + (.CLKA(CLKA), .RSTA(RSTA), .CEA(CEA), .WEA(WEA), .ADA(ADA), .DIA(DIA), .DOA(DOA), + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), .ADB(ADB), .DIB({DIB[16:9], DIB[7:0]}), .DOB({DOB[16:9], DOB[7:0]})); + end // else: !if(DATA_WIDTH_B == 9) + end // if (!DATA_WIDTH_HYBRID) + else begin : simple_multiple_ports + DP16KD__SPLIT_MUL#(.DATA_WIDTH_A(DATA_WIDTH_A), + .ADDR_WIDTH_A(ADDR_WIDTH_A), + .DATA_WIDTH_B(DATA_WIDTH_B), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ram + (.CLKA(CLKA), .RSTA(RSTA), .CEA(CEA), .WEA(WEA), .ADA(ADA), .DIA(DIA), .DOA(DOA), + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .WEB(WEB), .ADB(ADB), .DIB(DIB), .DOB(DOB)); + end // else: !if(!DATA_WIDTH_HYBRID) +endmodule + +module DP16KD( + input DIA17, DIA16, DIA15, DIA14, DIA13, DIA12, DIA11, DIA10, DIA9, DIA8, DIA7, DIA6, DIA5, DIA4, DIA3, DIA2, DIA1, DIA0, + input ADA13, ADA12, ADA11, ADA10, ADA9, ADA8, ADA7, ADA6, ADA5, ADA4, ADA3, ADA2, ADA1, ADA0, + input CEA, OCEA, CLKA, WEA, RSTA, + input CSA2, CSA1, CSA0, + output DOA17, DOA16, DOA15, DOA14, DOA13, DOA12, DOA11, DOA10, DOA9, DOA8, DOA7, DOA6, DOA5, DOA4, DOA3, DOA2, DOA1, DOA0, + + input DIB17, DIB16, DIB15, DIB14, DIB13, DIB12, DIB11, DIB10, DIB9, DIB8, DIB7, DIB6, DIB5, DIB4, DIB3, DIB2, DIB1, DIB0, + input ADB13, ADB12, ADB11, ADB10, ADB9, ADB8, ADB7, ADB6, ADB5, ADB4, ADB3, ADB2, ADB1, ADB0, + input CEB, OCEB, CLKB, WEB, RSTB, + input CSB2, CSB1, CSB0, + output DOB17, DOB16, DOB15, DOB14, DOB13, DOB12, DOB11, DOB10, DOB9, DOB8, DOB7, DOB6, DOB5, DOB4, DOB3, DOB2, DOB1, DOB0 +); + // Internal behavior of the RSTA/RSTB inputs. Regardless of mode, + // asserting RSTA/RSTB only clears the input/output registers for + // their respective port. The contents of the memory is unaffected. + // + // If RESETMODE=SYNC, RSTA and RSTB are only acted upon at + // positive-going edges of CLKA and CLKB, respectively. In this mode + // ASYNC_RESET_RELEASE is ignored. + // + // If RESETMODE=ASYNC and ASYNC_RESET_RELEASE=SYNC, asserting RSTA + // or RSTB is acted upon immediately, but release from reset is + // synchronized to positive-going edges of CLKA or CLKB, + // respectively. The caller must allow for a few clock cycles of + // settling time after release from reset, before the memory is + // ready for use. + // + // If RESETMODE=ASYNC and ASYNC_RESET_RELEASE=ASYNC, assertion and + // release of RSTA and RSTB are both acted upon immediately. In this + // mode, the caller is expected to explicitly sequence the reset to + // avoid metastable behavior: halt the port's clock, assert reset, + // wait for flops to settle, release reset, wait for flops to + // settle, re-enable port clock. + parameter RESETMODE = "SYNC"; + parameter ASYNC_RESET_RELEASE = "SYNC"; + + // The width of the input/output data busses, one of 1, 2, 4, 9 or + // 18. When less than 18, the unused DIA inputs are ignored and the + // unused DOA outputs are undefined. + parameter DATA_WIDTH_A = 18; + parameter DATA_WIDTH_B = 18; + + // Output registration. If "NOREG", only inputs are registered and + // operations have a latency of 1 cycle. If "OUTREG", an additional + // register is inserted on DOA, and operation latency is 2 + // cycles. Adding an output register significantly shortens the RAM + // datapath, and may enable higher operating frequency. + parameter REGMODE_A = "NOREG"; + parameter REGMODE_B = "NOREG"; + + // Chip select address. The memory only responds to inputs if the + // value of {CSA2, CSA1, CSA0} matches this address. This can be + // used to array DP16KD primitives into larger memories without + // needing external chip select circuitry. + parameter CSDECODE_A = "0b000"; + parameter CSDECODE_B = "0b000"; + + // Output behavior on write cycles. + // + // If "NORMAL", the output value is undefined during a write. + // + // If "WRITETHROUGH", the memory mirrors the value being written to + // the output. + // + // If "READBEFOREWRITE", the memory outputs the value that was just + // replaced by the write. + parameter WRITEMODE_A = "NORMAL"; + parameter WRITEMODE_B = "NORMAL"; + + // DP16KD has optional inverters on every input and output. Each + // *MUX parameter configures inversion of the corresponding + // input/output: a value of "" means use + // the signal as-is, "INV" means insert an inverter. + parameter DIA17MUX = "DIA17"; + parameter DIA16MUX = "DIA16"; + parameter DIA15MUX = "DIA15"; + parameter DIA14MUX = "DIA14"; + parameter DIA13MUX = "DIA13"; + parameter DIA12MUX = "DIA12"; + parameter DIA11MUX = "DIA11"; + parameter DIA10MUX = "DIA10"; + parameter DIA9MUX = "DIA9"; + parameter DIA8MUX = "DIA8"; + parameter DIA7MUX = "DIA7"; + parameter DIA6MUX = "DIA6"; + parameter DIA5MUX = "DIA5"; + parameter DIA4MUX = "DIA4"; + parameter DIA3MUX = "DIA3"; + parameter DIA2MUX = "DIA2"; + parameter DIA1MUX = "DIA1"; + parameter DIA0MUX = "DIA0"; + + parameter ADA13MUX = "ADA13"; + parameter ADA12MUX = "ADA12"; + parameter ADA11MUX = "ADA11"; + parameter ADA10MUX = "ADA10"; + parameter ADA9MUX = "ADA9"; + parameter ADA8MUX = "ADA8"; + parameter ADA7MUX = "ADA7"; + parameter ADA6MUX = "ADA6"; + parameter ADA5MUX = "ADA5"; + parameter ADA4MUX = "ADA4"; + parameter ADA3MUX = "ADA3"; + parameter ADA2MUX = "ADA2"; + parameter ADA1MUX = "ADA1"; + parameter ADA0MUX = "ADA0"; + + parameter DOA17MUX = "DOA17"; + parameter DOA16MUX = "DOA16"; + parameter DOA15MUX = "DOA15"; + parameter DOA14MUX = "DOA14"; + parameter DOA13MUX = "DOA13"; + parameter DOA12MUX = "DOA12"; + parameter DOA11MUX = "DOA11"; + parameter DOA10MUX = "DOA10"; + parameter DOA9MUX = "DOA9"; + parameter DOA8MUX = "DOA8"; + parameter DOA7MUX = "DOA7"; + parameter DOA6MUX = "DOA6"; + parameter DOA5MUX = "DOA5"; + parameter DOA4MUX = "DOA4"; + parameter DOA3MUX = "DOA3"; + parameter DOA2MUX = "DOA2"; + parameter DOA1MUX = "DOA1"; + parameter DOA0MUX = "DOA0"; + + parameter CSA2MUX = "CSA2"; + parameter CSA1MUX = "CSA1"; + parameter CSA0MUX = "CSA0"; + + parameter CEAMUX = "CEA"; + parameter OCEAMUX = "OCEA"; + parameter CLKAMUX = "CLKA"; + parameter WEAMUX = "WEA"; + parameter RSTAMUX = "RSTA"; + + parameter DIB17MUX = "DIB17"; + parameter DIB16MUX = "DIB16"; + parameter DIB15MUX = "DIB15"; + parameter DIB14MUX = "DIB14"; + parameter DIB13MUX = "DIB13"; + parameter DIB12MUX = "DIB12"; + parameter DIB11MUX = "DIB11"; + parameter DIB10MUX = "DIB10"; + parameter DIB9MUX = "DIB9"; + parameter DIB8MUX = "DIB8"; + parameter DIB7MUX = "DIB7"; + parameter DIB6MUX = "DIB6"; + parameter DIB5MUX = "DIB5"; + parameter DIB4MUX = "DIB4"; + parameter DIB3MUX = "DIB3"; + parameter DIB2MUX = "DIB2"; + parameter DIB1MUX = "DIB1"; + parameter DIB0MUX = "DIB0"; + + parameter ADB13MUX = "ADB13"; + parameter ADB12MUX = "ADB12"; + parameter ADB11MUX = "ADB11"; + parameter ADB10MUX = "ADB10"; + parameter ADB9MUX = "ADB9"; + parameter ADB8MUX = "ADB8"; + parameter ADB7MUX = "ADB7"; + parameter ADB6MUX = "ADB6"; + parameter ADB5MUX = "ADB5"; + parameter ADB4MUX = "ADB4"; + parameter ADB3MUX = "ADB3"; + parameter ADB2MUX = "ADB2"; + parameter ADB1MUX = "ADB1"; + parameter ADB0MUX = "ADB0"; + + parameter DOB17MUX = "DOB17"; + parameter DOB16MUX = "DOB16"; + parameter DOB15MUX = "DOB15"; + parameter DOB14MUX = "DOB14"; + parameter DOB13MUX = "DOB13"; + parameter DOB12MUX = "DOB12"; + parameter DOB11MUX = "DOB11"; + parameter DOB10MUX = "DOB10"; + parameter DOB9MUX = "DOB9"; + parameter DOB8MUX = "DOB8"; + parameter DOB7MUX = "DOB7"; + parameter DOB6MUX = "DOB6"; + parameter DOB5MUX = "DOB5"; + parameter DOB4MUX = "DOB4"; + parameter DOB3MUX = "DOB3"; + parameter DOB2MUX = "DOB2"; + parameter DOB1MUX = "DOB1"; + parameter DOB0MUX = "DOB0"; + + parameter CSB2MUX = "CSB2"; + parameter CSB1MUX = "CSB1"; + parameter CSB0MUX = "CSB0"; + + parameter CEBMUX = "CEB"; + parameter OCEBMUX = "OCEB"; + parameter CLKBMUX = "CLKB"; + parameter WEBMUX = "WEB"; + parameter RSTBMUX = "RSTB"; + + // WID is a RAM ID assigned during the synthesis process, so that + // the bitstream knows what data to load into each block RAM. + parameter WID = 0; + + // GSR is whether this block RAM is connected to the global reset + // signal. If "ENABLED", asserting GSR will do the same as asserting + // both RSTA and RSTB. If "DISABLED", asserting GSR does not affect + // the state of this RAM. + parameter GSR = "ENABLED"; + + // Initial value to load into the RAM at FPGA configuration + // time. Each 20 bit chunk maps to 18 bits of the RAM. The final 2 + // bits of each chunk are discarded. + parameter INITVAL_00 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_01 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_02 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_03 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_04 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_05 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_06 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_07 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_08 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_09 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_10 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_11 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_12 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_13 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_14 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_15 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_16 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_17 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_18 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_19 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_20 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_21 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_22 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_23 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_24 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_25 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_26 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_27 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_28 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_29 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_30 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_31 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_32 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_33 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_34 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_35 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_36 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_37 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_38 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_39 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + // Style of INITVAL encoding in the bitstream. "STATIC" means the + // initial value is inlined in the bitstream, "DYNAMIC" means the + // initial value is stored separately in the config flash and loaded + // by reference. + // + // This parameter only affects bitstream generation, and as such is + // ignored in this simulation model. + parameter INIT_DATA = "STATIC"; + + localparam ADDR_WIDTH_A = (DATA_WIDTH_A == 18 ? 10 : + DATA_WIDTH_A == 9 ? 11 : + DATA_WIDTH_A == 4 ? 12 : + DATA_WIDTH_A == 2 ? 13 : + DATA_WIDTH_A == 1 ? 14 : -1); + localparam ADDR_WIDTH_B = (DATA_WIDTH_B == 18 ? 10 : + DATA_WIDTH_B == 9 ? 11 : + DATA_WIDTH_B == 4 ? 12 : + DATA_WIDTH_B == 2 ? 13 : + DATA_WIDTH_B == 1 ? 14 : -1); + + localparam ADD_RESET_SYNCHRONIZER = RESETMODE == "ASYNC" && ASYNC_RESET_RELEASE == "SYNC"; + + ////////////////////////////////////////////////////// + // API cleanup + // + // For compatibility with Lattice Diamond, the shape of this + // primitive is pretty terrible: the input and output ports are + // exploded into individual bits, parameters that represent bit + // values are strings that need parsing, etc. + // + // Tidy things up in a separate section, so that the main logic of + // the module is hopefully easier to read. We convert string + // parameters into usable bit values, and reassemble the individual + // bits into ports of the correct widths, with inverters and X + // values inserted as appropriate. + // + // After this section, ADA, DIA, CSA, DOA are ports of the correct + // width, and {CLKA,RSTA,CEA,OCEA,WEA}_val are the single-bit inputs + // with optional inversion applied. + + localparam CSADDR_A = ((CSDECODE_A == "0b000") ? 3'b000 : + (CSDECODE_A == "0b001") ? 3'b001 : + (CSDECODE_A == "0b010") ? 3'b010 : + (CSDECODE_A == "0b011") ? 3'b011 : + (CSDECODE_A == "0b100") ? 3'b100 : + (CSDECODE_A == "0b101") ? 3'b101 : + (CSDECODE_A == "0b110") ? 3'b110 : + (CSDECODE_A == "0b111") ? 3'b111 : + 3'bxxx); + + wire [ADDR_WIDTH_A-1:0] ADA_in = {ADA13, ADA12, ADA11, ADA10, ADA9, ADA8, ADA7, + ADA6, ADA5, ADA4, ADA3, ADA2, ADA1, ADA0}; + wire [ADDR_WIDTH_A-1:0] ADA_invert = {ADA13MUX == "INV", ADA12MUX == "INV", + ADA11MUX == "INV", ADA10MUX == "INV", + ADA9MUX == "INV", ADA8MUX == "INV", + ADA7MUX == "INV", ADA6MUX == "INV", + ADA5MUX == "INV", ADA4MUX == "INV", + ADA3MUX == "INV", ADA2MUX == "INV", + ADA1MUX == "INV", ADA0MUX == "INV"}; + wire [ADDR_WIDTH_A-1:0] ADA = ADA_in ^ ADA_invert; + + wire [DATA_WIDTH_A-1:0] DIA_in = {DIA17, DIA16, DIA15, DIA14, DIA13, DIA12, DIA11, DIA10, DIA9, + DIA8, DIA7, DIA6, DIA5, DIA4, DIA3, DIA2, DIA1, DIA0}; + wire [DATA_WIDTH_A-1:0] DIA_invert = {DIA17MUX == "INV", DIA16MUX == "INV", DIA15MUX == "INV", + DIA14MUX == "INV", DIA13MUX == "INV", DIA12MUX == "INV", + DIA11MUX == "INV", DIA10MUX == "INV", DIA9MUX == "INV", + DIA8MUX == "INV", DIA7MUX == "INV", DIA6MUX == "INV", + DIA5MUX == "INV", DIA4MUX == "INV", DIA3MUX == "INV", + DIA2MUX == "INV", DIA1MUX == "INV", DIA0MUX == "INV"}; + wire [DATA_WIDTH_A-1:0] DIA = DIA_in ^ DIA_invert; + + wire [2:0] CSA_in = {CSA2, CSA1, CSA0}; + wire [2:0] CSA_invert = {CSA2MUX == "INV", CSA1MUX == "INV", CSA0MUX == "INV"}; + wire [2:0] CSA = CSA_in ^ CSA_invert; + + wire [DATA_WIDTH_A-1:0] DOA; + wire [DATA_WIDTH_A-1:0] DOA_invert = {DOA17MUX == "INV", DOA16MUX == "INV", DOA15MUX == "INV", + DOA14MUX == "INV", DOA13MUX == "INV", DOA12MUX == "INV", + DOA11MUX == "INV", DOA10MUX == "INV", DOA9MUX == "INV", + DOA8MUX == "INV", DOA7MUX == "INV", DOA6MUX == "INV", + DOA5MUX == "INV", DOA4MUX == "INV", DOA3MUX == "INV", + DOA2MUX == "INV", DOA1MUX == "INV", DOA0MUX == "INV"}; + wire [DATA_WIDTH_A-1:0] DOA_undef = ((DATA_WIDTH_A == 18) ? {18{1'b1}} : + (DATA_WIDTH_A == 9) ? {{9{1'bx}}, {9{1'b1}}} : + (DATA_WIDTH_A == 4) ? {{14{1'bx}}, {4{1'b1}}} : + (DATA_WIDTH_A == 2) ? {{16{1'bx}}, {2{1'b1}}} : + (DATA_WIDTH_A == 1) ? {{17{1'bx}}, 1'b1} : + {18{1'bx}}); + wire [DATA_WIDTH_A-1:0] DOA_out = (DOA ^ DOA_invert) & DOA_undef; + assign DOA17 = DOA_out[17]; assign DOA16 = DOA_out[16]; assign DOA15 = DOA_out[15]; + assign DOA14 = DOA_out[14]; assign DOA13 = DOA_out[13]; assign DOA12 = DOA_out[12]; + assign DOA11 = DOA_out[11]; assign DOA10 = DOA_out[10]; assign DOA9 = DOA_out[9]; + assign DOA8 = DOA_out[8]; assign DOA7 = DOA_out[7]; assign DOA6 = DOA_out[6]; + assign DOA5 = DOA_out[5]; assign DOA4 = DOA_out[4]; assign DOA3 = DOA_out[3]; + assign DOA2 = DOA_out[2]; assign DOA1 = DOA_out[1]; assign DOA0 = DOA_out[0]; + + wire CLK_A = CLKA ^ (CLKAMUX == "INV"); + wire CE_A = CEA ^ (CEAMUX == "INV"); + wire OCE_A = OCEA ^ (OCEAMUX == "INV"); + wire WE_A = WEA ^ (WEAMUX == "INV"); + wire RST_A_IN = RSTA ^ (RSTAMUX == "INV"); // Further modified by reset logic later + + localparam CSADDR_B = ((CSDECODE_B == "0b000") ? 3'b000 : + (CSDECODE_B == "0b001") ? 3'b001 : + (CSDECODE_B == "0b010") ? 3'b010 : + (CSDECODE_B == "0b011") ? 3'b011 : + (CSDECODE_B == "0b100") ? 3'b100 : + (CSDECODE_B == "0b101") ? 3'b101 : + (CSDECODE_B == "0b110") ? 3'b110 : + (CSDECODE_B == "0b111") ? 3'b111 : + 3'bxxx); + + wire [ADDR_WIDTH_B-1:0] ADB_in = {ADB13, ADB12, ADB11, ADB10, ADB9, ADB8, ADB7, + ADB6, ADB5, ADB4, ADB3, ADB2, ADB1, ADB0}; + wire [ADDR_WIDTH_B-1:0] ADB_invert = {ADB13MUX == "INV", ADB12MUX == "INV", + ADB11MUX == "INV", ADB10MUX == "INV", + ADB9MUX == "INV", ADB8MUX == "INV", + ADB7MUX == "INV", ADB6MUX == "INV", + ADB5MUX == "INV", ADB4MUX == "INV", + ADB3MUX == "INV", ADB2MUX == "INV", + ADB1MUX == "INV", ADB0MUX == "INV"}; + wire [ADDR_WIDTH_B-1:0] ADB = ADB_in ^ ADB_invert; + + wire [DATA_WIDTH_B-1:0] DIB_in = {DIB17, DIB16, DIB15, DIB14, DIB13, DIB12, DIB11, DIB10, DIB9, + DIB8, DIB7, DIB6, DIB5, DIB4, DIB3, DIB2, DIB1, DIB0}; + wire [DATA_WIDTH_B-1:0] DIB_invert = {DIB17MUX == "INV", DIB16MUX == "INV", DIB15MUX == "INV", + DIB14MUX == "INV", DIB13MUX == "INV", DIB12MUX == "INV", + DIB11MUX == "INV", DIB10MUX == "INV", DIB9MUX == "INV", + DIB8MUX == "INV", DIB7MUX == "INV", DIB6MUX == "INV", + DIB5MUX == "INV", DIB4MUX == "INV", DIB3MUX == "INV", + DIB2MUX == "INV", DIB1MUX == "INV", DIB0MUX == "INV"}; + wire [DATA_WIDTH_B-1:0] DIB = DIB_in ^ DIB_invert; + + wire [2:0] CSB_in = {CSB2, CSB1, CSB0}; + wire [2:0] CSB_invert = {CSB2MUX == "INV", CSB1MUX == "INV", CSB0MUX == "INV"}; + wire [2:0] CSB = CSB_in ^ CSB_invert; + + wire [DATA_WIDTH_B-1:0] DOB; // Assigned to by main logic, not here. + wire [DATA_WIDTH_B-1:0] DOB_invert = {DOB17MUX == "INV", DOB16MUX == "INV", DOB15MUX == "INV", + DOB14MUX == "INV", DOB13MUX == "INV", DOB12MUX == "INV", + DOB11MUX == "INV", DOB10MUX == "INV", DOB9MUX == "INV", + DOB8MUX == "INV", DOB7MUX == "INV", DOB6MUX == "INV", + DOB5MUX == "INV", DOB4MUX == "INV", DOB3MUX == "INV", + DOB2MUX == "INV", DOB1MUX == "INV", DOB0MUX == "INV"}; + wire [DATA_WIDTH_B-1:0] DOB_undef = ((DATA_WIDTH_B == 18) ? {18{1'b1}} : + (DATA_WIDTH_B == 9) ? {{9{1'b1}}, {9{1'b1}}} : + (DATA_WIDTH_B == 4) ? {{14{1'bx}}, {4{1'b1}}} : + (DATA_WIDTH_B == 2) ? {{16{1'bx}}, {2{1'b1}}} : + (DATA_WIDTH_B == 1) ? {{17{1'bx}}, 1'b1} : + {18{1'bx}}); + wire [DATA_WIDTH_B-1:0] DOB_out = (DOB ^ DOB_invert) & DOB_undef; + assign DOB17 = DOB_out[17]; assign DOB16 = DOB_out[16]; assign DOB15 = DOB_out[15]; + assign DOB14 = DOB_out[14]; assign DOB13 = DOB_out[13]; assign DOB12 = DOB_out[12]; + assign DOB11 = DOB_out[11]; assign DOB10 = DOB_out[10]; assign DOB9 = DOB_out[9]; + assign DOB8 = DOB_out[8]; assign DOB7 = DOB_out[7]; assign DOB6 = DOB_out[6]; + assign DOB5 = DOB_out[5]; assign DOB4 = DOB_out[4]; assign DOB3 = DOB_out[3]; + assign DOB2 = DOB_out[2]; assign DOB1 = DOB_out[1]; assign DOB0 = DOB_out[0]; + + wire CLK_B = CLKB ^ (CLKBMUX == "INV"); + wire CE_B = CEB ^ (CEBMUX == "INV"); + wire OCE_B = OCEB ^ (OCEBMUX == "INV"); + wire WE_B = WEB ^ (WEBMUX == "INV"); + wire RST_B_IN = RSTB ^ (RSTBMUX == "INV"); // Further modified by reset logic later + + ////////////////////////////////////////////////////// + // Reset control + // + // Sync vs. async reset assertion is handled inside sub-RAMs. If + // async assert with sync release was requested, insert a + // synchronizer here. + wire RST_A; + wire RST_B; + + if (ADD_RESET_SYNCHRONIZER) begin + reg RSTA_sync_1, RSTA_sync_2; + always @(posedge CLK_A, posedge RST_A_IN) begin + if (RST_A_IN) begin + RSTA_sync_1 <= 1'b1; + RSTA_sync_2 <= 1'b1; + end + else begin + RSTA_sync_1 <= 1'b0; + RSTA_sync_2 <= RSTA_sync_1; + end + end + assign RST_A = RSTA_sync_2; + + reg RSTB_sync_1, RSTB_sync_2; + always @(posedge CLK_B, posedge RST_B_IN) begin + if (RST_B_IN) begin + RSTB_sync_1 <= 1'b1; + RSTB_sync_2 <= 1'b1; + end + else begin + RSTB_sync_1 <= 1'b0; + RSTB_sync_2 <= RSTA_sync_1; + end + end + assign RST_B = RSTB_sync_2; + end // if (ADD_RESET_SYNCHRONIZER) + else begin + assign RST_A = RST_A_IN; + assign RST_B = RST_B_IN; + end + + ////////////////////////////////////////////////////// + // Main logic + // + // Now that we have clean signals, we can build the overall RAM up + // out of simple memories with equal size ports. + + // "unreg" is a bit of a misnomer, they are the values of the output + // registers in the elaborated __INTERNAL rams. Those registers + // represent DP16KD's _input_ latches, and thus these signals are + // unregistered outputs. + wire [DATA_WIDTH_A-1:0] DOA_unreg; + wire [DATA_WIDTH_B-1:0] DOB_unreg; + if (DATA_WIDTH_A > DATA_WIDTH_B) begin + DP16KD__DECOMPOSE#(.DATA_WIDTH_A(DATA_WIDTH_B), + .DATA_WIDTH_B(DATA_WIDTH_A), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_B), + .WRITEMODE_B(WRITEMODE_A)) ram + (.CLKA(CLK_B), .RSTA(RST_B), .CEA(CE_B && CSB == CSADDR_B), .WEA(WE_B), .ADA(ADB), .DIA(DIB), .DOA(DOB_unreg), + .CLKB(CLK_A), .RSTB(RST_A), .CEB(CE_A && CSA == CSADDR_A), .WEB(WE_A), .ADB(ADA), .DIB(DIA), .DOB(DOA_unreg)); + end + else begin + DP16KD__DECOMPOSE#(.DATA_WIDTH_A(DATA_WIDTH_A), + .DATA_WIDTH_B(DATA_WIDTH_B), + .RESETMODE(RESETMODE), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B(WRITEMODE_B)) ram + (.CLKA(CLK_A), .RSTA(RST_A), .CEA(CE_A && CSA == CSADDR_A), .WEA(WE_A), .ADA(ADA), .DIA(DIA), .DOA(DOA_unreg), + .CLKB(CLK_B), .RSTB(RST_B), .CEB(CE_B && CSB == CSADDR_B), .WEB(WE_B), .ADB(ADB), .DIB(DIB), .DOB(DOB_unreg)); + end + + ////////////////////////////////////////////////////// + // Output mapping + + if (REGMODE_A == "OUTREG") begin + reg [DATA_WIDTH_A-1:0] DOA_reg; + always @(posedge CLK_A) begin + if (OCE_A) + DOA_reg <= DOA_unreg; + end + assign DOA = DOA_reg; + end + else begin + assign DOA = DOA_unreg; + end + + if (REGMODE_B == "OUTREG") begin + reg [DATA_WIDTH_B-1:0] DOB_reg; + always @(posedge CLK_B) begin + if (OCE_B) + DOB_reg <= DOB_unreg; + end + assign DOB = DOB_reg; + end + else begin + assign DOB = DOB_unreg; + end +endmodule diff --git a/sim/tb/tb_DP16KD_18b_sync_nowriteout_unregistered.v b/sim/tb/tb_DP16KD_18b_sync_nowriteout_unregistered.v new file mode 100644 index 0000000..3ffb90c --- /dev/null +++ b/sim/tb/tb_DP16KD_18b_sync_nowriteout_unregistered.v @@ -0,0 +1,106 @@ +module tb_DP16KD_18b_sync_nowriteout_unregistered(); + reg CLKA=0, RSTA=0, CEA=0, OCEA=0, WEA=0; + reg CLKB=0, RSTB=0, CEB=0, OCEB=0, WEB=0; + reg [2:0] CSA=0, CSB=0; + reg [13:0] ADA, ADB; + reg [17:0] DIA, DIB; + wire [17:0] DOA, DOB; + reg [239:0] TESTNAME; + + DP16KD ram(.CLKA(CLKA), .RSTA(RSTA), .CEA(CEA), .OCEA(OCEA), .WEA(WEA), + .CSA2(CSA[2]), .CSA1(CSA[1]), .CSA0(CSA[0]), + .ADA13(ADA[13]), .ADA12(ADA[12]), .ADA11(ADA[11]), .ADA10(ADA[10]), .ADA9(ADA[9]), .ADA8(ADA[8]), .ADA7(ADA[7]), .ADA6(ADA[6]), .ADA5(ADA[5]), .ADA4(ADA[4]), .ADA3(ADA[3]), .ADA2(ADA[2]), .ADA1(ADA[1]), .ADA0(ADA[0]), + .DIA17(DIA[17]), .DIA16(DIA[16]), .DIA15(DIA[15]), .DIA14(DIA[14]), .DIA13(DIA[13]), .DIA12(DIA[12]), .DIA11(DIA[11]), .DIA10(DIA[10]), .DIA9(DIA[9]), .DIA8(DIA[8]), .DIA7(DIA[7]), .DIA6(DIA[6]), .DIA5(DIA[5]), .DIA4(DIA[4]), .DIA3(DIA[3]), .DIA2(DIA[2]), .DIA1(DIA[1]), .DIA0(DIA[0]), + .DOA17(DOA[17]), .DOA16(DOA[16]), .DOA15(DOA[15]), .DOA14(DOA[14]), .DOA13(DOA[13]), .DOA12(DOA[12]), .DOA11(DOA[11]), .DOA10(DOA[10]), .DOA9(DOA[9]), .DOA8(DOA[8]), .DOA7(DOA[7]), .DOA6(DOA[6]), .DOA5(DOA[5]), .DOA4(DOA[4]), .DOA3(DOA[3]), .DOA2(DOA[2]), .DOA1(DOA[1]), .DOA0(DOA[0]), + + .CLKB(CLKB), .RSTB(RSTB), .CEB(CEB), .OCEB(OCEB), .WEB(WEB), + .CSB2(CSB[2]), .CSB1(CSB[1]), .CSB0(CSB[0]), + .ADB13(ADB[13]), .ADB12(ADB[12]), .ADB11(ADB[11]), .ADB10(ADB[10]), .ADB9(ADB[9]), .ADB8(ADB[8]), .ADB7(ADB[7]), .ADB6(ADB[6]), .ADB5(ADB[5]), .ADB4(ADB[4]), .ADB3(ADB[3]), .ADB2(ADB[2]), .ADB1(ADB[1]), .ADB0(ADB[0]), + .DIB17(DIB[17]), .DIB16(DIB[16]), .DIB15(DIB[15]), .DIB14(DIB[14]), .DIB13(DIB[13]), .DIB12(DIB[12]), .DIB11(DIB[11]), .DIB10(DIB[10]), .DIB9(DIB[9]), .DIB8(DIB[8]), .DIB7(DIB[7]), .DIB6(DIB[6]), .DIB5(DIB[5]), .DIB4(DIB[4]), .DIB3(DIB[3]), .DIB2(DIB[2]), .DIB1(DIB[1]), .DIB0(DIB[0]), + .DOB17(DOB[17]), .DOB16(DOB[16]), .DOB15(DOB[15]), .DOB14(DOB[14]), .DOB13(DOB[13]), .DOB12(DOB[12]), .DOB11(DOB[11]), .DOB10(DOB[10]), .DOB9(DOB[9]), .DOB8(DOB[8]), .DOB7(DOB[7]), .DOB6(DOB[6]), .DOB5(DOB[5]), .DOB4(DOB[4]), .DOB3(DOB[3]), .DOB2(DOB[2]), .DOB1(DOB[1]), .DOB0(DOB[0])); + + always begin + #5 + CLKA <= !CLKA; + CLKB <= !CLKB; + end + + initial begin + $dumpfile("tb_DP16KD_18b_sync_nowriteout_unregistered"); + $dumpvars(0, tb_DP16KD_18b_sync_nowriteout_unregistered); + #10 + // Write to lowest and highest addrs, read back from the other port. + TESTNAME="SIMPLE WRITE/READ 1"; + ADA=0; DIA=42; CEA=1; WEA=1; // Write min addr + ADB=1023; DIB=18'h3FFFF; CEB=1; WEB=1; // write max addr + #10 + ADA=1023; DIA=0; CEA=1; WEA=0; // Read max addr + ADB=0; DIB=0; CEB=1; WEB=0; // Read min addr + #10 + // Swap values around, read back from other port. + TESTNAME="SIMPLE WRITE/READ 2"; + ADA=1023; DIA=42; CEA=1; WEA=1; // Write max addr + ADB=0; DIB=18'h3FFFF; CEB=1; WEB=1; // Write min addr + #10 + ADA=0; DIA=0; CEA=1; WEA=0; // Read min addr + ADB=1023; DIB=0; CEB=1; WEB=0; // Read max addr + #10 + // No change when reading and not enabled + TESTNAME="NOT ENABLED"; + ADA=1023; DIA=0; CEA=0; WEA=0; + ADB=0; DIA=0; CEB=0; WEB=0; + #10 + // Output changes again with chip enabled + CEA=1; + CEB=1; + #10 + // Same if another chip is selected + TESTNAME="NOT SELECTED"; + ADA=1023; DIA=0; WEA=0; CSA=3; + ADB=0; DIB=0; WEB=0; CSB=2; + #10 + // Output changes again with chip enabled + CSA=0; + CSB=0; + #10 + // Reset clears regs, overrules input, doesn't affect other port, + // doesn't affect memory contents. + TESTNAME="RESET A"; + ADA=0; RSTA=1; + #10 + RSTA=0; + #10 + TESTNAME="RESET B"; + ADB=1023; RSTB=1; + #10 + RSTB=0; + #10 + // Write-write conflict writes undef value + TESTNAME="WRITE/WRITE CONFLICT"; + ADA=0; DIA=0; WEA=1; + ADB=0; DIB=18'h3FFFF; WEB=1; + #10 + WEA=0; + WEB=0; + #10 + // Write-write conflict writes undef value + TESTNAME="A READ/B WRITE CONFLICT"; + ADA=0; DIA=0; WEA=0; + ADB=0; DIB=18'h3FFFF; WEB=1; + #10 + WEA=0; + WEB=0; + #10 + // Write-write conflict writes undef value + TESTNAME="A WRITE/B READ CONFLICT"; + ADA=0; DIA=0; WEA=1; + ADB=0; DIB=18'h3FFFF; WEB=0; + #10 + WEA=0; + WEB=0; + #10 + TESTNAME=240'bx; + #10 + $finish; + end +endmodule