From 9db87cb8ee2a38f78f605658fe23f0b2022262b2 Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Sun, 21 Jan 2024 04:38:34 +0000 Subject: [PATCH] bram: integrate into SoC using Wishbone bus, and note alignment --- gateware/rtl/bram/bram.v.m4 | 41 +++++++++++++++++++++---------------- gateware/soc.py | 27 ++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/gateware/rtl/bram/bram.v.m4 b/gateware/rtl/bram/bram.v.m4 index 0bb31b9..d5dec1b 100644 --- a/gateware/rtl/bram/bram.v.m4 +++ b/gateware/rtl/bram/bram.v.m4 @@ -6,12 +6,9 @@ m4_changecom(⟨/*⟩, ⟨*/⟩) * For license terms, refer to the files in `doc/copying` in the Upsilon * source distribution. * - * BRAM to Wishbone interface. + * This BRAM can only handle aligned accesses. */ -module bram_interface #( - /* This is the last INDEX of the word array, which is indexed in - * words, not octets. */ - parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047, +module bram #( /* Width of the memory bus */ parameter BUS_WID = 32, /* Width of a request. */ @@ -26,37 +23,45 @@ module bram_interface #( input wb_we, input [4-1:0] wb_sel, input [BUS_WID-1:0] wb_addr, - input [BUS_WID-1:0] wb_data_i, + input [BUS_WID-1:0] wb_dat_w, output reg wb_ack, - output wb_stall, - output reg [BUS_WID-1:0] wb_data_o, + output reg [BUS_WID-1:0] wb_dat_r, ); -assign wb_stall = wb_ack; +/* When the size of the memory is a power of 2, the mask is the + * last addressable index in the array. + * + * Since this buffer stores words, this is divided by 4 (32 bits). + * When accessing a single byte, the address + * 0b......Xab + * is shifted to the right by two bits, throwing away "ab". This indexes + * the 32 bit word that contains the address. This applies to halfwords + * and words as long as the accesses are aligned. + */ +reg [WORD_WID-1:0] buffer [(ADDR_MASK >> 2):0]; -reg [BUS_WID-1:0] buffer [WORD_AMNT:0]; +/* Current index into the buffer. */ +wire [13-1:0] ind = (wb_addr & ADDR_MASK) >> 2; m4_define(⟨bufwrite⟩, ⟨begin - buffer[mem_addr & ADDR_MASK] <= - (buffer[wb_addr & ADDR_MASK] & $1) - | wb_data_i[$2]; + buffer[ind] <= (buffer[ind] & $1) | wb_dat_w[$2]; end⟩) always @ (posedge clk) if (wb_cyc && wb_stb && !wb_ack) if (!wb_we) begin - wb_data_o <= buffer[wb_addr & ADDR_MASK]; + wb_dat_r <= buffer[ind]; wb_ack <= 1; end else begin wb_ack <= 1; case (wb_sel) - 4'b1111: buffer[wb_addr & ADDR_MASK] <= wb_data_o; + 4'b1111: buffer[ind] <= wb_dat_w; + 4'b0011: bufwrite(32'hFFFF0000, 15:0) + 4'b1100: bufwrite(32'h0000FFFF, 31:16) 4'b0001: bufwrite(32'hFFFFFF00, 7:0) 4'b0010: bufwrite(32'hFFFF00FF, 15:8) - 4'b0011: bufwrite(32'hFFFF0000, 15:0) 4'b0100: bufwrite(32'hFF00FFFF, 23:16) 4'b1000: bufwrite(32'h00FFFFFF, 31:24) - 4'b1100: bufwrite(32'h0000FFFF, 31:16) - default: mem_ready <= 1; + default: ; endcase end else if (!wb_stb) begin diff --git a/gateware/soc.py b/gateware/soc.py index 6a4f84f..b2b55da 100644 --- a/gateware/soc.py +++ b/gateware/soc.py @@ -51,6 +51,7 @@ from litex.soc.integration.soc_core import SoCCore from litex.soc.integration.soc import SoCRegion from litex.soc.cores.clock import S7PLL, S7IDELAYCTRL from litex.soc.interconnect.csr import AutoCSR, Module, CSRStorage, CSRStatus +from litex.soc.interconnect.wishbone import Interface from litedram.phy import s7ddrphy from litedram.modules import MT41K128M16 @@ -183,6 +184,25 @@ class Base(Module, AutoCSR): self.specials += Instance("base", **self.kwargs) +class BRAM(Module): + def __init__(self, clk): + self.bus = Interface(data_width=32, address_width=32, addressing="byte") + self.comb += [ + self.bus.cti.eq(0), + self.bus.bte.eq(0), + ] + self.specials += Instance("bram", + i_clk = clk, + i_wb_cyc = self.bus.cyc, + i_wb_stb = self.bus.stb, + i_wb_we = self.bus.we, + i_wb_sel = self.bus.sel, + i_wb_addr = self.bus.adr, + i_wb_dat_w = self.bus.dat_w, + o_wb_ack = self.bus.ack, + o_wb_dat_r = self.bus.dat_r, + ) + # Clock and Reset Generator # I don't know how this works, I only know that it does. class _CRG(Module): @@ -222,6 +242,8 @@ class UpsilonSoC(SoCCore): self.add_constant(f"{ip_name}{seg_num}", int(ip_byte)) def add_bram(self, region_name): self.bus.add_region(region_name, SoCRegion(size=0x2000, cached=False)) + # TODO: special name + self.submodules.bram0 = BRAM(ClockSignal()) def __init__(self, variant="a7-100", @@ -259,7 +281,8 @@ class UpsilonSoC(SoCCore): platform.add_source("rtl/control_loop/control_loop.v") # platform.add_source("rtl/waveform/bram_interface_preprocessed.v") # platform.add_source("rtl/waveform/waveform_preprocessed.v") - platform.add_source("rtl/bram/bram_preprocessed.v") +# when SoC cannot find a source file, it will fail with a confusing error message + platform.add_source("rtl/bram/bram.v") platform.add_source("rtl/base/base.v") # SoCCore does not have sane defaults (no integrated rom) @@ -307,7 +330,7 @@ class UpsilonSoC(SoCCore): self.add_ip(remote_ip, "REMOTEIP") self.add_constant("TFTP_SERVER_PORT", tftp_port) - self.add_bram("BRAM0") + self.add_bram("bram0") # Add pins platform.add_extension(io)