diff --git a/src/src/axi/BetterAxiMaster.scala b/src/src/axi/BetterAxiMaster.scala index 95d03498..017ffbff 100644 --- a/src/src/axi/BetterAxiMaster.scala +++ b/src/src/axi/BetterAxiMaster.scala @@ -58,9 +58,10 @@ class BetterAxiMaster( // Note: Burst length is AxLEN + 1 assert(readSize % (bytesPerTransfer * byteLength) == 0) assert(writeSize % (bytesPerTransfer * byteLength) == 0) + val readLen = WireDefault((readSize / (bytesPerTransfer * byteLength)).U) val writeLen = WireDefault((writeSize / (bytesPerTransfer * byteLength)).U) assert(writeLen <= "b_1111_1111".U) - io.axi.ar.bits.len := ((readSize / (bytesPerTransfer * byteLength)) - 1).U + io.axi.ar.bits.len := readLen - 1.U io.axi.aw.bits.len := writeLen - 1.U // Set others @@ -80,11 +81,16 @@ class BetterAxiMaster( // Size per transfer in bits val transferSize = bytesPerTransfer * 8 + // Max burst count + val maxBurstCount = readLen - 1.U + // Handle read val isReadingReg = RegInit(false.B) isReadingReg := isReadingReg // Fallback: Keep state - val readDataReg = RegInit(0.U(readSize.W)) + val readDataReg = RegInit(0.U(readSize.W)) + val readBurstCountReg = RegInit(maxBurstCount) + readBurstCountReg := readBurstCountReg val nextReadData = WireDefault(readDataReg) readDataReg := nextReadData // Fallback: Keep data val isReadReady = WireDefault(!isReadingReg && io.axi.ar.ready) @@ -105,9 +111,10 @@ class BetterAxiMaster( when(io.read.req.isValid && isReadReady) { // Accept request - isReadFailedNext := false.B - isReadingReg := true.B - nextReadData := 0.U + isReadFailedNext := false.B + isReadingReg := true.B + nextReadData := 0.U + readBurstCountReg := maxBurstCount } when(isReadingReg) { @@ -131,7 +138,9 @@ class BetterAxiMaster( } } - when(io.axi.r.bits.last) { + readBurstCountReg := readBurstCountReg - 1.U + + when(!readBurstCountReg.orR) { // Reading complete io.read.res.isValid := true.B io.read.res.data := nextReadData diff --git a/src/src/memory/VBRam.scala b/src/src/memory/VBRam.scala index a19932c5..9de047b1 100644 --- a/src/src/memory/VBRam.scala +++ b/src/src/memory/VBRam.scala @@ -27,26 +27,49 @@ class VSingleBRam(size: Int, dataWidth: Int) extends Module { class VTrueDualBRam(size: Int, dataWidth: Int) extends Module { // TODO: customize it as you want - val io = IO(new Bundle { - }) + val io = IO(new Bundle {}) val blackBox = Module(new truedual_readfirst_bram(size, dataWidth)) - blackBox.io.addra := DontCare - blackBox.io.addrb := DontCare - blackBox.io.dina := DontCare - blackBox.io.dinb := DontCare - blackBox.io.clka := DontCare - blackBox.io.wea := DontCare - blackBox.io.web := DontCare - blackBox.io.ena := DontCare - blackBox.io.enb := DontCare - blackBox.io.rsta := DontCare - blackBox.io.rstb := DontCare + blackBox.io.addra := DontCare + blackBox.io.addrb := DontCare + blackBox.io.dina := DontCare + blackBox.io.dinb := DontCare + blackBox.io.clka := DontCare + blackBox.io.wea := DontCare + blackBox.io.web := DontCare + blackBox.io.ena := DontCare + blackBox.io.enb := DontCare + blackBox.io.rsta := DontCare + blackBox.io.rstb := DontCare blackBox.io.regcea := DontCare blackBox.io.regceb := DontCare - DontCare <> blackBox.io.douta - DontCare <> blackBox.io.doutb + DontCare <> blackBox.io.douta + DontCare <> blackBox.io.doutb +} + +class VSimpleDualBRam(size: Int, dataWidth: Int) extends Module { + val addrWidth = log2Ceil(size) + + val io = IO(new Bundle { + val isWrite = Input(Bool()) + val readAddr = Input(UInt(addrWidth.W)) + val writeAddr = Input(UInt(addrWidth.W)) + val dataIn = Input(UInt(dataWidth.W)) + val dataOut = Output(UInt(dataWidth.W)) + }) + + val blackBox = Module(new simpledual_readfirst_bram(size, dataWidth)) + + blackBox.io.addra := io.readAddr + blackBox.io.addrb := io.writeAddr + blackBox.io.dina := io.dataIn + blackBox.io.clka := clock + blackBox.io.wea := io.isWrite + blackBox.io.enb := true.B + blackBox.io.rstb := reset + blackBox.io.regceb := false.B + io.dataOut := blackBox.io.doutb } class single_readfirst_bram(size: Int, dataWidth: Int) @@ -70,13 +93,13 @@ class single_readfirst_bram(size: Int, dataWidth: Int) } class truedual_readfirst_bram(size: Int, dataWidth: Int) - extends BlackBox( - Map( - "RAM_WIDTH" -> dataWidth, - "RAM_DEPTH" -> size, - "RAM_PERFORMANCE" -> "LOW_LATENCY" - ) - ) { + extends BlackBox( + Map( + "RAM_WIDTH" -> dataWidth, + "RAM_DEPTH" -> size, + "RAM_PERFORMANCE" -> "LOW_LATENCY" + ) + ) { val io = IO(new Bundle { val addra = Input(UInt(log2Ceil(size).W)) val addrb = Input(UInt(log2Ceil(size).W)) @@ -96,3 +119,23 @@ class truedual_readfirst_bram(size: Int, dataWidth: Int) }) } +class simpledual_readfirst_bram(size: Int, dataWidth: Int) + extends BlackBox( + Map( + "RAM_WIDTH" -> dataWidth, + "RAM_DEPTH" -> size, + "RAM_PERFORMANCE" -> "LOW_LATENCY" + ) + ) { + val io = IO(new Bundle { + val addra = Input(UInt(log2Ceil(size).W)) + val addrb = Input(UInt(log2Ceil(size).W)) + val dina = Input(UInt(dataWidth.W)) + val clka = Input(Clock()) + val wea = Input(Bool()) + val enb = Input(Bool()) + val rstb = Input(Bool()) + val regceb = Input(Bool()) + val doutb = Output(UInt(dataWidth.W)) + }) +} diff --git a/verilog/simpledual_readfirst_bram.v b/verilog/simpledual_readfirst_bram.v new file mode 100644 index 00000000..db5d878c --- /dev/null +++ b/verilog/simpledual_readfirst_bram.v @@ -0,0 +1,76 @@ +// Xilinx Simple Dual Port Single Clock RAM +// This code implements a parameterizable SDP single clock memory. +// If a reset or enable is not necessary, it may be tied off or removed from the code. + +module simpledual_readfirst_bram #( + parameter RAM_WIDTH = 1, // Specify RAM data width + parameter RAM_DEPTH = 1, // Specify RAM depth (number of entries) + parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE", // Select "HIGH_PERFORMANCE" or "LOW_LATENCY" + parameter INIT_FILE = "" // Specify name/location of RAM initialization file if using one (leave blank if not) +) ( + input [clogb2(RAM_DEPTH-1)-1:0] addra, // Write address bus, width determined from RAM_DEPTH + input [clogb2(RAM_DEPTH-1)-1:0] addrb, // Read address bus, width determined from RAM_DEPTH + input [RAM_WIDTH-1:0] dina, // RAM input data + input clka, // Clock + input wea, // Write enable + input enb, // Read Enable, for additional power savings, disable when not in use + input rstb, // Output reset (does not affect memory contents) + input regceb, // Output register enable + output [RAM_WIDTH-1:0] doutb // RAM output data +); + + reg [RAM_WIDTH-1:0] BRAM [RAM_DEPTH-1:0]; + reg [RAM_WIDTH-1:0] ram_data = {RAM_WIDTH{1'b0}}; + + // The following code either initializes the memory values to a specified file or to all zeros to match hardware + generate + if (INIT_FILE != "") begin: use_init_file + initial + $readmemh(INIT_FILE, BRAM, 0, RAM_DEPTH-1); + end else begin: init_bram_to_zero + integer ram_index; + initial + for (ram_index = 0; ram_index < RAM_DEPTH; ram_index = ram_index + 1) + BRAM[ram_index] = {RAM_WIDTH{1'b0}}; + end + endgenerate + + always @(posedge clka) begin + if (wea) + BRAM[addra] <= dina; + if (enb) + ram_data <= BRAM[addrb]; + end + + // The following code generates HIGH_PERFORMANCE (use output register) or LOW_LATENCY (no output register) + generate + if (RAM_PERFORMANCE == "LOW_LATENCY") begin: no_output_register + + // The following is a 1 clock cycle read latency at the cost of a longer clock-to-out timing + assign doutb = ram_data; + + end else begin: output_register + + // The following is a 2 clock cycle read latency with improve clock-to-out timing + + reg [RAM_WIDTH-1:0] doutb_reg = {RAM_WIDTH{1'b0}}; + + always @(posedge clka) + if (rstb) + doutb_reg <= {RAM_WIDTH{1'b0}}; + else if (regceb) + doutb_reg <= ram_data; + + assign doutb = doutb_reg; + + end + endgenerate + + // The following function calculates the address width based on specified RAM depth + function integer clogb2; + input integer depth; + for (clogb2=0; depth>0; clogb2=clogb2+1) + depth = depth >> 1; + endfunction + +endmodule \ No newline at end of file diff --git a/verilog/single_readfirst_bram.v b/verilog/single_readfirst_bram.v index f2ac9055..8022e2bb 100644 --- a/verilog/single_readfirst_bram.v +++ b/verilog/single_readfirst_bram.v @@ -1,10 +1,10 @@ - // Xilinx Single Port Read First RAM - // This code implements a parameterizable single-port read-first memory where when data - // is written to the memory, the output reflects the prior contents of the memory location. - // If the output data is not needed during writes or the last read value is desired to be - // retained, it is suggested to set WRITE_MODE to NO_CHANGE as it is more power efficient. - // If a reset or enable is not necessary, it may be tied off or removed from the code. - // Modify the parameters for the desired RAM characteristics. +// Xilinx Single Port Read First RAM +// This code implements a parameterizable single-port read-first memory where when data +// is written to the memory, the output reflects the prior contents of the memory location. +// If the output data is not needed during writes or the last read value is desired to be +// retained, it is suggested to set WRITE_MODE to NO_CHANGE as it is more power efficient. +// If a reset or enable is not necessary, it may be tied off or removed from the code. +// Modify the parameters for the desired RAM characteristics. module single_readfirst_bram # ( diff --git a/verilog/truedual_readfirst_bram.v b/verilog/truedual_readfirst_bram.v index e12013e5..f79cba2c 100644 --- a/verilog/truedual_readfirst_bram.v +++ b/verilog/truedual_readfirst_bram.v @@ -1,4 +1,3 @@ - // Xilinx True Dual Port RAM Read First Single Clock // This code implements a parameterizable true dual port memory (both ports can read and write). // The behavior of this RAM is when data is written, the prior memory contents at the write @@ -6,6 +5,7 @@ // not needed during writes or the last read value is desired to be retained, // it is suggested to use a no change RAM as it is more power efficient. // If a reset or enable is not necessary, it may be tied off or removed from the code. + module truedual_readfirst_bram #( parameter RAM_WIDTH = 88, // Specify RAM data width parameter RAM_DEPTH = 1024, // Specify RAM depth (number of entries)