From eba9764645042a30e3b25034c1a616101c428998 Mon Sep 17 00:00:00 2001 From: myrtle Date: Mon, 2 Mar 2026 10:10:55 +0100 Subject: [PATCH] xilinx: Import timings for BRAM (#1653) Signed-off-by: gatecat --- himbaechel/uarch/xilinx/gen/parse_sdf.py | 11 ++++ himbaechel/uarch/xilinx/gen/xilinx_gen.py | 78 +++++++++++++++++++++++ himbaechel/uarch/xilinx/xilinx.cc | 11 ++++ 3 files changed, 100 insertions(+) diff --git a/himbaechel/uarch/xilinx/gen/parse_sdf.py b/himbaechel/uarch/xilinx/gen/parse_sdf.py index 2eea49ec..178f14af 100644 --- a/himbaechel/uarch/xilinx/gen/parse_sdf.py +++ b/himbaechel/uarch/xilinx/gen/parse_sdf.py @@ -112,6 +112,8 @@ def parse_sdf_file(filename): else: inst = "top" cell = CellData(celltype, inst) + setups = {} + holds = {} for subentry in entry[3:]: if subentry[0] == "DELAY": assert subentry[1][0] == "ABSOLUTE" @@ -128,7 +130,16 @@ def parse_sdf_file(filename): if check[0] == "SETUPHOLD": cell.entries.append( SetupHoldCheck(check[1], check[2], parse_delay(check[3][0]), parse_delay(check[4][0]))) + elif check[0] == "SETUP": + setups[(check[1], check[2][1])] = parse_delay(check[3][0]) + elif check[0] == "HOLD": + holds[(check[1], check[2][1])] = parse_delay(check[3][0]) elif check[0] == "WIDTH": cell.entries.append(WidthCheck(check[1], parse_delay(check[2][0]))) + # merge setups and holds + for k, v in setups.items(): + if k not in holds: + continue + cell.entries.append(SetupHoldCheck(k[0], k[1], v, holds[k])) sdf.cells[(celltype, inst)] = cell return sdf diff --git a/himbaechel/uarch/xilinx/gen/xilinx_gen.py b/himbaechel/uarch/xilinx/gen/xilinx_gen.py index 79ee4356..a8a5b6d2 100644 --- a/himbaechel/uarch/xilinx/gen/xilinx_gen.py +++ b/himbaechel/uarch/xilinx/gen/xilinx_gen.py @@ -261,6 +261,82 @@ def import_sdf_timings(variant, sdfcell): TimingValue(int(min(entry.rising.minv, entry.falling.minv)*1000), int(max(entry.rising.maxv, entry.falling.maxv)*1000))) +def import_bram_timings(timing, sdf): + def import_bus_sethold(cell, port, width, clock, entry): + for i in range(width): + cell.add_setup_hold(clock, f"{port}{i}", ClockEdge.RISING, TimingValue(int(entry.setup.minv*1000), int(entry.setup.maxv*1000)), + TimingValue(int(entry.hold.minv*1000), int(entry.hold.maxv*1000))) + + def import_bus_clkq(cell, port, width, clock, entry): + for i in range(width): + cell.add_clock_out(clock, f"{port}{i}", ClockEdge.RISING, TimingValue(int(min(entry.rising.minv, entry.falling.minv)*1000), + int(max(entry.rising.maxv, entry.falling.maxv)*1000))) + + + def import_pin_sethold(cell, port, clock, entry): + cell.add_setup_hold(clock, f"{port}", ClockEdge.RISING, TimingValue(int(entry.setup.minv*1000), int(entry.setup.maxv*1000)), + TimingValue(int(entry.hold.minv*1000), int(entry.hold.maxv*1000))) + + for wsdp, rsdp in ((False, False), (False, True), (True, False), (True, True)): + bram18 = timing.add_cell_variant("DEFAULT", f"RAMB18E1_RAMB18E1_{'WSDP' if wsdp else 'WTDP'}_{'RSDP' if rsdp else 'RTDP'}") + + for entry in sdf.cells[("RAMBFIFO36E1", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.SetupHoldCheck): + if entry.pin == "ADDRAU": import_bus_sethold(bram18, "ADDRARDADDR", 14, "CLKARDCLK", entry) + if entry.pin == "ADDRBU": import_bus_sethold(bram18, "ADDRBWRADDR", 14, "CLKBWRCLK", entry) + if entry.pin == "WEAU": import_bus_sethold(bram18, "WEA", 4, "CLKARDCLK", entry) + if entry.pin == "WEBU": import_bus_sethold(bram18, "WEBWE", 8, "CLKBWRCLK", entry) + for entry in sdf.cells[("RAMBFIFO36E1_ISFIFO_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.SetupHoldCheck): + if entry.pin == "ENARDENU": import_pin_sethold(bram18, "ENARDEN", "CLKARDCLK", entry) + if entry.pin == "ENBWRENU": import_pin_sethold(bram18, "ENBWREN", "CLKBWRCLK", entry) + if entry.pin == "RSTRAMAU": import_pin_sethold(bram18, "RSTRAMARSTRAM", "CLKARDCLK", entry) + if entry.pin == "RSTRAMBU": import_pin_sethold(bram18, "RSTRAMB", "CLKBWRCLK", entry) + for entry in sdf.cells[("RAMBFIFO36E1RAM_MODE_RAMB18TDP_U_WRITE_MODE_U_NC_EN_ECC_READ_FALSE_EN_ECC_WRITE_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.SetupHoldCheck): + if entry.pin == "DIADIU": import_bus_sethold(bram18, "DIADI", 16, "CLKBWRCLK" if wsdp else "CLKARDCLK", entry) + if entry.pin == "DIBDIU": import_bus_sethold(bram18, "DIBDI", 16, "CLKBWRCLK", entry) + if entry.pin == "DIPADIPU": import_bus_sethold(bram18, "DIPADIP", 2, "CLKBWRCLK" if wsdp else "CLKARDCLK", entry) + if entry.pin == "DIPBDIPU": import_bus_sethold(bram18, "DIPBDIP", 2, "CLKBWRCLK", entry) + for entry in sdf.cells[("RAMBFIFO36E1RAM_MODE_U_RAMB18SDP_U_DOA_REG_U_0_EN_ECC_READ_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.IOPath): + if entry.to_pin == "DOADOU": import_bus_clkq(bram18, "DOADO", 16, "CLKARDCLK", entry) + if entry.to_pin == "DOPADOPU": import_bus_clkq(bram18, "DOPADOP", 2, "CLKARDCLK", entry) + for entry in sdf.cells[("RAMBFIFO36E1RAM_MODE_U_RAMB18TDP_U_DOB_REG_U_0_EN_ECC_READ_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.IOPath): + if entry.to_pin == "DOBDOU": import_bus_clkq(bram18, "DOBDO", 16, "CLKARDCLK" if rsdp else "CLKBWRCLK", entry) + if entry.to_pin == "DOPBDOPU": import_bus_clkq(bram18, "DOPBDOP", 2, "CLKARDCLK" if rsdp else "CLKBWRCLK", entry) + + bram36 = timing.add_cell_variant("DEFAULT", f"RAMB36E1_RAMB36E1_{'WSDP' if wsdp else 'WTDP'}_{'RSDP' if rsdp else 'RTDP'}") + + for u in ('L', 'U'): + for entry in sdf.cells[("RAMBFIFO36E1", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.SetupHoldCheck): + if entry.pin == "ADDRAU": import_bus_sethold(bram36, f"ADDRARDADDR{u}", 15 if u == 'L' else 14, "CLKARDCLK", entry) + if entry.pin == "ADDRBU": import_bus_sethold(bram36, f"ADDRBWRADDR{u}", 15 if u == 'L' else 14, "CLKBWRCLK", entry) + if entry.pin == "WEAU": import_bus_sethold(bram36, f"WEA{u}", 4, f"CLKARDCLK{u}", entry) + if entry.pin == "WEBU": import_bus_sethold(bram36, f"WEBWE{u}", 8, f"CLKBWRCLK{u}", entry) + for entry in sdf.cells[("RAMBFIFO36E1_ISFIFO_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.SetupHoldCheck): + if entry.pin == "ENARDENU": import_pin_sethold(bram36, f"ENARDEN{u}", f"CLKARDCLK{u}", entry) + if entry.pin == "ENBWRENU": import_pin_sethold(bram36, f"ENBWREN{u}", f"CLKBWRCLK{u}", entry) + if entry.pin == "RSTRAMAU": import_pin_sethold(bram36, f"RSTRAMARSTRAM{u}", f"CLKARDCLK{u}", entry) + if entry.pin == "RSTRAMBU": import_pin_sethold(bram36, f"RSTRAMB{u}", f"CLKBWRCLK{u}", entry) + for entry in sdf.cells[("RAMBFIFO36E1RAM_MODE_RAMB18TDP_U_WRITE_MODE_U_NC_EN_ECC_READ_FALSE_EN_ECC_WRITE_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.SetupHoldCheck): + if entry.pin == "DIADIU": import_bus_sethold(bram36, "DIADI", 32, "CLKBWRCLK" if wsdp else "CLKARDCLK", entry) + if entry.pin == "DIBDIU": import_bus_sethold(bram36, "DIBDI", 32, "CLKBWRCLK", entry) + if entry.pin == "DIPADIPU": import_bus_sethold(bram36, "DIPADIP", 4, "CLKBWRCLK" if wsdp else "CLKARDCLK", entry) + if entry.pin == "DIPBDIPU": import_bus_sethold(bram36, "DIPBDIP", 4, "CLKBWRCLK", entry) + for entry in sdf.cells[("RAMBFIFO36E1RAM_MODE_U_RAMB18SDP_U_DOA_REG_U_0_EN_ECC_READ_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.IOPath): + if entry.to_pin == "DOADOU": import_bus_clkq(bram36, "DOADO", 32, "CLKARDCLK", entry) + if entry.to_pin == "DOPADOPU": import_bus_clkq(bram36, "DOPADOP", 4, "CLKARDCLK", entry) + for entry in sdf.cells[("RAMBFIFO36E1RAM_MODE_U_RAMB18TDP_U_DOB_REG_U_0_EN_ECC_READ_FALSE", "RAMBFIFO36E1")].entries: + if isinstance(entry, parse_sdf.IOPath): + if entry.to_pin == "DOBDOU": import_bus_clkq(bram36, "DOBDO", 32, "CLKARDCLK" if rsdp else "CLKBWRCLK", entry) + if entry.to_pin == "DOPBDOPU": import_bus_clkq(bram36, "DOPBDOP", 4, "CLKARDCLK" if rsdp else "CLKBWRCLK", entry) + def main(): xlbase = path.join(path.dirname(path.realpath(__file__)), "..") @@ -376,6 +452,8 @@ def main(): carry = ch.timing.add_cell_variant("DEFAULT", "CARRY4") import_sdf_timings(carry, slicem_sdf.cells[("CARRY4", "SLICEM")]) + import_bram_timings(ch.timing, parse_sdf.parse_sdf_file(path.join(timings_root, "timings", "BRAM_L.sdf"))) + # Import package pins for package_name, package in sorted(d.packages.items(), key=lambda x:x[0]): pkg = ch.create_package(package_name) diff --git a/himbaechel/uarch/xilinx/xilinx.cc b/himbaechel/uarch/xilinx/xilinx.cc index 27bd8e54..f97ad447 100644 --- a/himbaechel/uarch/xilinx/xilinx.cc +++ b/himbaechel/uarch/xilinx/xilinx.cc @@ -429,6 +429,17 @@ void XilinxImpl::assign_cell_tags() ct.carry.x_sigs[i] = nullptr; } ct.carry.x_sigs[0] = ci->getPort(id_CYINIT); + } else if (ci->type == id_RAMB18E1_RAMB18E1 || ci->type == id_RAMB36E1_RAMB36E1) { + bool read_sdp = ((ci->type == id_RAMB18E1_RAMB18E1 && + int_or_default(ci->params, ctx->id("READ_WIDTH_B"), 0) == 36) || + (ci->type == id_RAMB36E1_RAMB36E1 && + int_or_default(ci->params, ctx->id("READ_WIDTH_B"), 0) == 72)); + bool write_sdp = ((ci->type == id_RAMB18E1_RAMB18E1 && + int_or_default(ci->params, ctx->id("WRITE_WIDTH_B"), 0) == 36) || + (ci->type == id_RAMB36E1_RAMB36E1 && + int_or_default(ci->params, ctx->id("WRITE_WIDTH_B"), 0) == 72)); + ci->timing_index = ctx->get_cell_timing_idx( + ctx->idf("%s_%s_%s", ci->type.c_str(ctx), write_sdp ? "WSDP" : "WTDP", read_sdp ? "RSDP" : "RTDP")); } } }