Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/src/axi/BetterAxiMaster.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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) {
Expand All @@ -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
Expand Down
87 changes: 65 additions & 22 deletions src/src/memory/VBRam.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand All @@ -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))
})
}
76 changes: 76 additions & 0 deletions verilog/simpledual_readfirst_bram.v
Original file line number Diff line number Diff line change
@@ -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
14 changes: 7 additions & 7 deletions verilog/single_readfirst_bram.v
Original file line number Diff line number Diff line change
@@ -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 #
(
Expand Down
2 changes: 1 addition & 1 deletion verilog/truedual_readfirst_bram.v
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

// 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
// address are presented on the output port. If the output data is
// 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)
Expand Down