diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..aa47c1c6 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:github.com)", + "Bash(buck2 cquery:*)", + "Bash(BSV_LIB_DIR=/custom/test/path buck2 audit config:*)" + ] + } +} diff --git a/hdl/ip/bsv/BUCK b/hdl/ip/bsv/BUCK index 43b5cdc1..2d56e7ac 100644 --- a/hdl/ip/bsv/BUCK +++ b/hdl/ip/bsv/BUCK @@ -31,7 +31,7 @@ bsv_library( srcs = ["SettableCRC.bsv"], ) -# Generate git version BSV file +# Generate git version BSV file (legacy, kept for cobble compatibility) genrule( name = "git_version_bsv", visibility = ["PUBLIC"], @@ -40,13 +40,37 @@ genrule( cmd_exe = "echo Not supported on Windows", ) -# bsv_library +# bsv_library (legacy, kept for cobble compatibility) bsv_library( visibility = ["PUBLIC"], name = "GitVersion", srcs = [":git_version_bsv"], ) +# BRAM-backed version ROM for post-P&R stamping. Replaces GitVersion +# in bitstream flows so that version data doesn't invalidate the cache. +bsv_library( + visibility = ["PUBLIC"], + name = "VersionROM", + srcs = ["VersionROM.bsv"], +) + +export_file( + name = "version_rom_template_hex", + src = "version_rom_template.hex", + visibility = ["PUBLIC"], +) + +# Volatile genrule: generates replacement hex with real git data. +# Only consumed by bitstream rules, so cache invalidation is limited +# to the final stamp+pack steps. +genrule( + name = "version_rom_replacement_hex", + visibility = ["PUBLIC"], + out = "version_rom_replacement.hex", + bash = "python3 $(location //tools:gen_version_hex) $OUT", +) + # bsv_library bsv_library( visibility = ["PUBLIC"], diff --git a/hdl/ip/bsv/I2C/BUCK b/hdl/ip/bsv/I2C/BUCK index e5761718..ee1f317c 100644 --- a/hdl/ip/bsv/I2C/BUCK +++ b/hdl/ip/bsv/I2C/BUCK @@ -1,30 +1,5 @@ load("//tools:bsv.bzl", "bsv_bluesim_tests") load("//tools:bsv.bzl", "bsv_library") -load("//tools:rdl.bzl", "rdl_file") - -# rdl_file -# Note: Buck2 RDL rule enforces strict naming convention -# BSV outputs use "Regs" suffix, VHDL uses "_pkg" suffix -rdl_file( - visibility = ["PUBLIC"], - name = "I2CCore_rdl", - outputs = [ - "I2CCoreRegs.bsv", - "I2CCore.html", - "I2CCore.json", - ], - src = "I2CCore.rdl", -) - -# bsv_library -bsv_library( - visibility = ["PUBLIC"], - name = "I2CCoreRegs", - srcs = [":I2CCore_rdl"], - deps = [ - "//hdl/ip/bsv:RegCommon", - ], -) # bsv_library bsv_library( diff --git a/hdl/ip/bsv/I2C/I2CCore.rdl b/hdl/ip/bsv/I2C/I2CCore.rdl deleted file mode 100644 index d058fe83..00000000 --- a/hdl/ip/bsv/I2C/I2CCore.rdl +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2022 Oxide Computer Company -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -addrmap I2C_core { - name = "I2C Core Registers"; - desc = "Register description for the I2C Core's control registers"; - - default regwidth = 8; - default sw = rw; - default hw = r; - - reg { - name = "Address Byte"; - - field { - desc = "Address to transmit"; - } ADDR[7:1] = 0; - - field { - desc = "Read/write bit. '1' if reading from peripheral, '0' if writing to it."; - } RW[0:0] = 0; - } ADDRESS; - - reg { - name = "Receive data"; - default sw = r; - default hw = rw; - - field { - desc = "Last byte recieved"; - } DATA[7:0] = 0; - } RECEIVE; - - reg { - name = "Transmit data"; - default sw = r; - default hw = rw; - - field { - desc = "Next byte to send"; - } DATA[7:0] = 0; - } TRANSMIT; - - reg { - name = "Control bits for I2C Core"; - - field { - desc = "Begin a transaction"; - } START[0:0] = 0; - } CONTROL; - - reg { - name = "Status"; - default sw = r; - default hw = rw; - - field { - desc = "I2C bus is busy. '1' after a START, '0' after a STOP."; - } ERR[0:0] = 0; - - field { - desc = "'1' while core is idling, '0' otherwise."; - } DONE[1:1] = 0; - - field { - desc = "'1' while core is in a transaction, '0' otherwise"; - } BUSY[0:0] = 0; - } STATUS; -}; \ No newline at end of file diff --git a/hdl/ip/bsv/VersionROM.bsv b/hdl/ip/bsv/VersionROM.bsv new file mode 100644 index 00000000..7f01b37f --- /dev/null +++ b/hdl/ip/bsv/VersionROM.bsv @@ -0,0 +1,62 @@ +// Copyright 2025 Oxide Computer Company +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package VersionROM; + +export VersionROMIfc(..); +export mkVersionROM; + +import Vector::*; + +// Public interface matching the original git_version package layout. +// version[0] is the LSB of the commit count, version[3] is MSB. +// sha[0] is the LSB of the short hash, sha[3] is MSB. +interface VersionROMIfc; + method Vector#(4, Bit#(8)) version; + method Vector#(4, Bit#(8)) sha; +endinterface + +// Raw BVI interface matching the Verilog output ports. +interface VersionROM_BVI; + method Bit#(32) version_raw; + method Bit#(32) sha_raw; + method Bool ready; +endinterface + +import "BVI" VersionROM = + module vVersionROM(VersionROM_BVI); + default_clock clk(CLK, (* unused *) CLK_GATE); + no_reset; + + method VERSION version_raw(); + method SHA sha_raw(); + method READY ready(); + + schedule (version_raw) CF (version_raw, sha_raw, ready); + schedule (sha_raw) CF (sha_raw, ready); + schedule (ready) CF (ready); + endmodule + +// Wrapper that converts Bit#(32) outputs to Vector#(4, Bit#(8)). +// The ROM stores bytes in big-endian order (MSB at lowest address), +// so we reverse after unpack to put the LSB at index 0, matching the +// original git_version package convention. +// No implicit conditions — values are zero for the first 9 clock +// cycles after reset, then hold the ROM contents (sentinel pattern +// until post-P&R stamping replaces them with real git data). +module mkVersionROM(VersionROMIfc); + VersionROM_BVI rom <- vVersionROM; + + method Vector#(4, Bit#(8)) version; + return reverse(unpack(rom.version_raw)); + endmethod + + method Vector#(4, Bit#(8)) sha; + return reverse(unpack(rom.sha_raw)); + endmethod +endmodule + +endpackage diff --git a/hdl/ip/bsv/VersionROM.v b/hdl/ip/bsv/VersionROM.v new file mode 100644 index 00000000..25968220 --- /dev/null +++ b/hdl/ip/bsv/VersionROM.v @@ -0,0 +1,82 @@ +// Copyright 2025 Oxide Computer Company +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// Version ROM for post-P&R stamping. +// +// A 256x8 BRAM ROM initialized with sentinel values. After place and +// route, icebram/ecpbram replaces the sentinel pattern with real git +// version and SHA data. This avoids volatile genrules that invalidate +// the build cache on every commit. +// +// Memory layout: +// [0:3] - Version (commit count), sentinel: DE AD BE EF +// [4:7] - SHA (short git hash), sentinel: CA FE BA BE +// [8:255] - Reserved (zeros) +// +// On startup, a small FSM reads bytes 0-7 from the BRAM and latches +// them into output registers. READY asserts after 9 clock cycles. + +module VersionROM( + input CLK, + output [31:0] VERSION, + output [31:0] SHA, + output READY +); + +(* ram_style = "block" *) +reg [7:0] mem [0:255]; + +initial begin : init_mem + integer i; + for (i = 0; i < 256; i = i + 1) + mem[i] = 8'h00; + // Version sentinel + mem[0] = 8'hDE; + mem[1] = 8'hAD; + mem[2] = 8'hBE; + mem[3] = 8'hEF; + // SHA sentinel + mem[4] = 8'hCA; + mem[5] = 8'hFE; + mem[6] = 8'hBA; + mem[7] = 8'hBE; +end + +reg [3:0] cnt = 4'd0; +reg [7:0] rom_rdata; +reg [31:0] version_r = 32'd0; +reg [31:0] sha_r = 32'd0; +reg ready_r = 1'b0; + +// BRAM read: address driven by counter, data available next cycle +always @(posedge CLK) + rom_rdata <= mem[cnt[2:0]]; + +// Latch bytes into output registers as they arrive from BRAM +always @(posedge CLK) begin + if (!ready_r) begin + case (cnt) + 4'd1: version_r[31:24] <= rom_rdata; + 4'd2: version_r[23:16] <= rom_rdata; + 4'd3: version_r[15:8] <= rom_rdata; + 4'd4: version_r[7:0] <= rom_rdata; + 4'd5: sha_r[31:24] <= rom_rdata; + 4'd6: sha_r[23:16] <= rom_rdata; + 4'd7: sha_r[15:8] <= rom_rdata; + 4'd8: begin + sha_r[7:0] <= rom_rdata; + ready_r <= 1'b1; + end + endcase + cnt <= cnt + 4'd1; + end +end + +assign VERSION = version_r; +assign SHA = sha_r; +assign READY = ready_r; + +endmodule diff --git a/hdl/ip/bsv/version_rom_template.hex b/hdl/ip/bsv/version_rom_template.hex new file mode 100644 index 00000000..fc0a97f3 --- /dev/null +++ b/hdl/ip/bsv/version_rom_template.hex @@ -0,0 +1,256 @@ +DE +AD +BE +EF +CA +FE +BA +BE +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 diff --git a/hdl/ip/vhd/info/BUCK b/hdl/ip/vhd/info/BUCK index ad43fbf7..d12a08d3 100644 --- a/hdl/ip/vhd/info/BUCK +++ b/hdl/ip/vhd/info/BUCK @@ -8,17 +8,13 @@ rdl_file( visibility = ['PUBLIC'] ) -# Janky generate git sha via genrule -# There are a lot of better ways this might be done, but this was the simplest. -# It does mean there's a re-build for any change to the git repo, but for now that's fine. -# Longer-term, we might evaluate backannotating ROMs or something with this build info -# This also breaks windows due to shell use +# Legacy volatile genrule (kept for backward compatibility) genrule( name = "git_sha", out = "git_sha_pkg.vhd", default_outs = ["git_sha_pkg.vhd"], cmd = '''echo "library ieee;\nuse ieee.std_logic_1164.all;\npackage git_sha_pkg is\n constant short_sha : std_logic_vector(31 downto 0) := X\\""`git rev-parse --short=8 HEAD`\\"";\nend package git_sha_pkg;\" > $OUT''', - + ) vhdl_unit( @@ -27,12 +23,20 @@ vhdl_unit( visibility = ['PUBLIC'] ) +# BRAM-backed version ROM for post-P&R stamping +vhdl_unit( + name = "version_rom", + srcs = ["version_rom.vhd"], + standard = "2008", + visibility = ['PUBLIC'] +) + # 2008-based signals in the this block vhdl_unit( name = "info_2k8", srcs = ["info_2k8.vhd"], deps = [ - ":git_sha_pkg", + ":version_rom", ":info_regs_rdl", "//hdl/ip/vhd/axi_blocks:axilite_if_2k8", ], diff --git a/hdl/ip/vhd/info/info_2k8.vhd b/hdl/ip/vhd/info/info_2k8.vhd index 1c59b163..dcbcaaea 100644 --- a/hdl/ip/vhd/info/info_2k8.vhd +++ b/hdl/ip/vhd/info/info_2k8.vhd @@ -12,7 +12,6 @@ use ieee.numeric_std.all; use ieee.numeric_std_unsigned.all; use work.info_regs_pkg.all; -use work.git_sha_pkg.all; entity info_2k8 is generic( @@ -55,7 +54,8 @@ end entity; architecture rtl of info_2k8 is constant identity : identity_type := rec_reset; - constant git_info : git_info_type := (sha => short_sha); + signal short_sha : std_logic_vector(31 downto 0); + signal git_info : git_info_type; signal checksum : fpga_checksum_type := rec_reset; signal scratchpad : scratchpad_type := rec_reset; signal hubris_compat: hubris_compat_type := rec_reset; @@ -64,6 +64,15 @@ architecture rtl of info_2k8 is begin + git_info <= (sha => short_sha); + + version_rom_inst: entity work.version_rom + port map( + clk => clk, + reset => reset, + short_sha => short_sha + ); + axil_target_txn_inst: entity work.axil_target_txn port map( clk => clk, diff --git a/hdl/ip/vhd/info/version_rom.vhd b/hdl/ip/vhd/info/version_rom.vhd new file mode 100644 index 00000000..52997974 --- /dev/null +++ b/hdl/ip/vhd/info/version_rom.vhd @@ -0,0 +1,90 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- +-- Copyright 2025 Oxide Computer Company + +-- BRAM-backed version ROM for post-P&R stamping. +-- +-- A 256x8 block RAM initialized with sentinel values. After place +-- and route, icebram/ecpbram (Yosys flows) or TCL init (Vivado) +-- replaces the sentinel pattern with real git version and SHA data. +-- +-- Memory layout (big-endian byte order): +-- [0:3] - Version (commit count), sentinel: DE AD BE EF +-- [4:7] - SHA (short git hash), sentinel: CA FE BA BE +-- [8:255] - Reserved (zeros) +-- +-- On startup, a small FSM reads bytes 4-7 from the BRAM and latches +-- them into the short_sha output register. The output is valid after +-- 9 clock cycles. + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity version_rom is + port ( + clk : in std_logic; + reset : in std_logic; + short_sha : out std_logic_vector(31 downto 0) + ); +end entity; + +architecture rtl of version_rom is + + type rom_type is array (0 to 255) of std_logic_vector(7 downto 0); + + signal rom : rom_type := ( + -- Version sentinel + 0 => x"DE", 1 => x"AD", 2 => x"BE", 3 => x"EF", + -- SHA sentinel + 4 => x"CA", 5 => x"FE", 6 => x"BA", 7 => x"BE", + others => x"00" + ); + attribute ram_style : string; + attribute ram_style of rom : signal is "block"; + + signal cnt : unsigned(3 downto 0) := x"0"; + signal rom_rdata : std_logic_vector(7 downto 0) := x"00"; + signal sha_r : std_logic_vector(31 downto 0) := (others => '0'); + signal ready : std_logic := '0'; + +begin + + -- BRAM read: address driven by counter, data available next cycle + bram_read : process(clk) + begin + if rising_edge(clk) then + rom_rdata <= rom(to_integer(cnt(2 downto 0))); + end if; + end process; + + -- Latch SHA bytes from BRAM into output register. + -- Bytes 4-7 of the ROM contain the SHA in big-endian order. + -- At cnt=5, rom_rdata holds mem[4] (from previous cycle's read). + latch : process(clk) + begin + if rising_edge(clk) then + if reset = '1' then + cnt <= x"0"; + sha_r <= (others => '0'); + ready <= '0'; + elsif ready = '0' then + case to_integer(cnt) is + when 5 => sha_r(31 downto 24) <= rom_rdata; + when 6 => sha_r(23 downto 16) <= rom_rdata; + when 7 => sha_r(15 downto 8) <= rom_rdata; + when 8 => + sha_r(7 downto 0) <= rom_rdata; + ready <= '1'; + when others => null; + end case; + cnt <= cnt + 1; + end if; + end if; + end process; + + short_sha <= sha_r; + +end rtl; diff --git a/hdl/projects/cosmo_hp/BUCK b/hdl/projects/cosmo_hp/BUCK index cae126a4..198cc979 100644 --- a/hdl/projects/cosmo_hp/BUCK +++ b/hdl/projects/cosmo_hp/BUCK @@ -67,5 +67,7 @@ ice40_bitstream( top= ":cosmo_hp_top", family="hx8k", package="ct256", - pinmap="cosmo_hp.pcf" + pinmap="cosmo_hp.pcf", + version_template_hex = "//hdl/ip/bsv:version_rom_template_hex", + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) \ No newline at end of file diff --git a/hdl/projects/cosmo_seq/BUCK b/hdl/projects/cosmo_seq/BUCK index dd5b0060..1dfcf83a 100644 --- a/hdl/projects/cosmo_seq/BUCK +++ b/hdl/projects/cosmo_seq/BUCK @@ -74,4 +74,5 @@ vivado_bitstream( constraints=glob(["*.xdc"]), pre_synth_tcl_files=glob(["xilinx_ip_gen/*.tcl"]), #post_synth_tcl_files=glob(["*ila.tcl"]), + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) \ No newline at end of file diff --git a/hdl/projects/gimlet/sequencer/AllEnable.bsv b/hdl/projects/gimlet/sequencer/AllEnable.bsv index 7a671150..11fd1b39 100644 --- a/hdl/projects/gimlet/sequencer/AllEnable.bsv +++ b/hdl/projects/gimlet/sequencer/AllEnable.bsv @@ -1,12 +1,14 @@ package AllEnable; import Connectable::*; +import Vector::*; // Cobalt-provided stuff import ICE40::*; import SPI::*; import GimletRegs::*; +import VersionROM::*; interface SpiPeripheralPinsTop; (* prefix = "" *) @@ -116,8 +118,10 @@ module mkGimletPowerSeqTop (Pins); SpiPeripheralSync spi_sync <- mkSpiPeripheralPinSync(); SpiPeripheralPhy phy <- mkSpiPeripheralPhy(); SpiDecodeIF decode <- mkSpiRegDecode(); - // Regiser block - GimletRegIF regs <- mkGimletRegs(); + // Version ROM (BRAM, stamped post-P&R) + VersionROMIfc ver_rom <- mkVersionROM; + // Register block + GimletRegIF regs <- mkGimletRegs(ver_rom.version, ver_rom.sha); // SPI mkConnection(spi_sync.syncd_pins, phy.pins); // Output of spi synchronizer to SPI PHY block (just pins interface) diff --git a/hdl/projects/gimlet/sequencer/BUCK b/hdl/projects/gimlet/sequencer/BUCK index 8262307b..a9c91fb1 100644 --- a/hdl/projects/gimlet/sequencer/BUCK +++ b/hdl/projects/gimlet/sequencer/BUCK @@ -39,7 +39,6 @@ bsv_library( name = "GimletRegs", srcs = ["GimletRegs.bsv"], deps = [ - "//hdl/ip/bsv:GitVersion", ":IrqBlock", "//hdl/ip/bsv:RegCommon", ":GimletSeqFpgaRegs", @@ -173,6 +172,7 @@ bsv_library( srcs = ["GimletTopIOSync.bsv"], deps = [ "//hdl/ip/bsv/interfaces:ICE40", + "//hdl/ip/bsv:VersionROM", ":GimletSeqTop", ], bsc_flags = [ @@ -203,6 +203,7 @@ bsv_library( deps = [ "//hdl/ip/bsv/interfaces:SPI", "//hdl/ip/bsv/interfaces:ICE40", + "//hdl/ip/bsv:VersionROM", ":GimletRegs", ], bsc_flags = [ @@ -249,6 +250,8 @@ bsv_nextpnr_ice40_bitstream( package = "ct256", pinmap = "gimlet_sequencer.pcf", nextpnr_args = [], + version_template_hex = "//hdl/ip/bsv:version_rom_template_hex", + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) # Alternative power sequencer design @@ -268,4 +271,6 @@ bsv_nextpnr_ice40_bitstream( package = "ct256", pinmap = "gimlet_sequencer.pcf", # Same pinmap as gimlet_sequencer nextpnr_args = [], + version_template_hex = "//hdl/ip/bsv:version_rom_template_hex", + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) diff --git a/hdl/projects/gimlet/sequencer/GimletRegs.bsv b/hdl/projects/gimlet/sequencer/GimletRegs.bsv index 9c619587..e1b37d68 100644 --- a/hdl/projects/gimlet/sequencer/GimletRegs.bsv +++ b/hdl/projects/gimlet/sequencer/GimletRegs.bsv @@ -9,9 +9,9 @@ import ConfigReg::*; import StmtFSM::*; // Oxide imports +import Vector::*; import IrqBlock::*; import RegCommon::*; -import git_version::*; import GimletSeqFpgaRegs::*; import NicBlock::*; // import EarlyPowerBlock::*; @@ -36,7 +36,7 @@ interface GimletRegIF; interface RegPins pins; endinterface -module mkGimletRegs(GimletRegIF); +module mkGimletRegs#(Vector#(4, Bit#(8)) ver, Vector#(4, Bit#(8)) sha)(GimletRegIF); // Registers ConfigReg#(Id0) id0 <- mkReg(defaultValue); ConfigReg#(Id1) id1 <- mkReg(defaultValue); @@ -187,10 +187,10 @@ module mkGimletRegs(GimletRegIF); case (address) fromInteger(id0Offset) : readdata <= tagged Valid (pack(id0)); fromInteger(id1Offset) : readdata <= tagged Valid (pack(id1)); - fromInteger(ver0Offset) : readdata <= tagged Valid (version[0]); - fromInteger(ver1Offset) : readdata <= tagged Valid (version[1]); - fromInteger(ver2Offset) : readdata <= tagged Valid (version[2]); - fromInteger(ver3Offset) : readdata <= tagged Valid (version[3]); + fromInteger(ver0Offset) : readdata <= tagged Valid (ver[0]); + fromInteger(ver1Offset) : readdata <= tagged Valid (ver[1]); + fromInteger(ver2Offset) : readdata <= tagged Valid (ver[2]); + fromInteger(ver3Offset) : readdata <= tagged Valid (ver[3]); fromInteger(sha0Offset) : readdata <= tagged Valid (sha[0]); fromInteger(sha1Offset) : readdata <= tagged Valid (sha[1]); fromInteger(sha2Offset) : readdata <= tagged Valid (sha[2]); diff --git a/hdl/projects/gimlet/sequencer/GimletSeqTop.bsv b/hdl/projects/gimlet/sequencer/GimletSeqTop.bsv index 547dba4d..832c548a 100644 --- a/hdl/projects/gimlet/sequencer/GimletSeqTop.bsv +++ b/hdl/projects/gimlet/sequencer/GimletSeqTop.bsv @@ -41,16 +41,18 @@ endinterface // // This is the "inner-top" module, meaning we expect everything to already be synchronized. // -module mkGimletInnerTop #(GimletSeqTopParameters parameters) (InnerTop); +module mkGimletInnerTop #(GimletSeqTopParameters parameters, + Vector#(4, Bit#(8)) ver, + Vector#(4, Bit#(8)) sha) (InnerTop); // SPI interface SpiPeripheralSync spi_sync <- mkSpiPeripheralPinSync(); SpiPeripheralPhy phy <- mkSpiPeripheralPhy(); SpiDecodeIF decode <- mkSpiRegDecode(); mkConnection(spi_sync.syncd_pins, phy.pins); mkConnection(decode.spi_byte, phy.decoder_if); // Output of the SPI PHY block to the SPI decoder block (client/server interface) - + // Register block - GimletRegIF regs <- mkGimletRegs(); + GimletRegIF regs <- mkGimletRegs(ver, sha); mkConnection(decode.reg_con, regs.decoder_if); // Client of SPI decoder to Server of registers block. // State machine blocks // NicBlockTop nic_block <- mkNicBlock(parameters.one_ms_counts); @@ -209,8 +211,10 @@ endinterface module mkBench(Bench); let sim_params = GimletSeqTopParameters {one_ms_counts: 500}; // Speed up sim time - - InnerTop dut <- mkGimletInnerTop(sim_params); + // Sentinel values for simulation (real data stamped post-P&R) + Vector#(4, Bit#(8)) ver = reverse(unpack(32'hDEADBEEF)); + Vector#(4, Bit#(8)) sha = reverse(unpack(32'hCAFEBABE)); + InnerTop dut <- mkGimletInnerTop(sim_params, ver, sha); // SPI controller ModelSpiController controller <- mkModelSpiController(); diff --git a/hdl/projects/gimlet/sequencer/GimletTopIOSync.bsv b/hdl/projects/gimlet/sequencer/GimletTopIOSync.bsv index c4a4c478..1cc08ab5 100644 --- a/hdl/projects/gimlet/sequencer/GimletTopIOSync.bsv +++ b/hdl/projects/gimlet/sequencer/GimletTopIOSync.bsv @@ -9,6 +9,7 @@ import ICE40::*; import SPI::*; // Local stuff +import Vector::*; import GimletSeqTop::*; import GimletSeqFpgaRegs::*; import A1Block::*; @@ -16,6 +17,7 @@ import A0Block::*; import NicBlock::*; import PowerRail::*; import GimletRegs::*; +import VersionROM::*; interface SpiPeripheralPinsTop; @@ -306,7 +308,8 @@ module mkGimletSeqTop (SeqPins); InputSync sync <- mkInputSync(); - let inner <- mkGimletInnerTop(synth_params, reset_by reset_sync); + VersionROMIfc ver_rom <- mkVersionROM(reset_by reset_sync); + let inner <- mkGimletInnerTop(synth_params, ver_rom.version, ver_rom.sha, reset_by reset_sync); ConfigReg#(BoardRev) brd_rev <- mkConfigRegU(); diff --git a/hdl/projects/grapefruit/BUCK b/hdl/projects/grapefruit/BUCK index 931d9bc2..765e6fe5 100644 --- a/hdl/projects/grapefruit/BUCK +++ b/hdl/projects/grapefruit/BUCK @@ -94,6 +94,7 @@ vivado_bitstream( constraints=glob(["*.xdc"]), pre_synth_tcl_files=glob(["*ip.tcl"]), #post_synth_tcl_files=glob(["*ila.tcl"]), + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) python_library( diff --git a/hdl/projects/sidecar/mainboard/BUCK b/hdl/projects/sidecar/mainboard/BUCK index 73a3c3f0..87f449da 100644 --- a/hdl/projects/sidecar/mainboard/BUCK +++ b/hdl/projects/sidecar/mainboard/BUCK @@ -120,7 +120,7 @@ bsv_library( ":TofinoDebugPort", "//hdl/ip/bsv/ignition:Controller", "//hdl/ip/bsv/ignition:ControllerRegisters", - "//hdl/ip/bsv:GitVersion", + "//hdl/ip/bsv:VersionROM", "//hdl/ip/bsv:RegCommon", "//hdl/ip/bsv:WriteOnceReg", ], @@ -179,6 +179,8 @@ bsv_nextpnr_ecp5_bitstream( package = "CABGA554", pinmap = "sidecar_mainboard_controller.lpf", nextpnr_args = ["--speed", "6", "--freq", "50"], + version_template_hex = "//hdl/ip/bsv:version_rom_template_hex", + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) # Rev B synthesis @@ -199,4 +201,6 @@ bsv_nextpnr_ecp5_bitstream( package = "CABGA554", pinmap = "sidecar_mainboard_controller_rev_b.lpf", nextpnr_args = ["--speed", "6", "--freq", "50"], + version_template_hex = "//hdl/ip/bsv:version_rom_template_hex", + version_replacement_hex = "//hdl/ip/bsv:version_rom_replacement_hex", ) diff --git a/hdl/projects/sidecar/mainboard/SidecarMainboardControllerSpiServer.bsv b/hdl/projects/sidecar/mainboard/SidecarMainboardControllerSpiServer.bsv index 72133e12..b4f480e8 100644 --- a/hdl/projects/sidecar/mainboard/SidecarMainboardControllerSpiServer.bsv +++ b/hdl/projects/sidecar/mainboard/SidecarMainboardControllerSpiServer.bsv @@ -13,7 +13,7 @@ import GetPut::*; import OInt::*; import Vector::*; -import git_version::*; +import VersionROM::*; import RegCommon::*; import WriteOnceReg::*; @@ -55,6 +55,8 @@ module mkSpiServer #( Add#(TLog#(n_ignition_controllers), b__, 8), // Less than 40 Ignition Controllers. Add#(n_ignition_controllers, c__, 40)); + VersionROMIfc ver_rom <- mkVersionROM; + Wire#(SpiRequest) in <- mkWire(); Wire#(SpiResponse) out <- mkWire(); @@ -156,16 +158,16 @@ module mkSpiServer #( fromOffset(cs3Offset): read(checksum[3]); // Version - fromOffset(version0Offset): read(version[0]); - fromOffset(version1Offset): read(version[1]); - fromOffset(version2Offset): read(version[2]); - fromOffset(version3Offset): read(version[3]); + fromOffset(version0Offset): read(ver_rom.version[0]); + fromOffset(version1Offset): read(ver_rom.version[1]); + fromOffset(version2Offset): read(ver_rom.version[2]); + fromOffset(version3Offset): read(ver_rom.version[3]); // SHA - fromOffset(sha0Offset): read(sha[0]); - fromOffset(sha1Offset): read(sha[1]); - fromOffset(sha2Offset): read(sha[2]); - fromOffset(sha3Offset): read(sha[3]); + fromOffset(sha0Offset): read(ver_rom.sha[0]); + fromOffset(sha1Offset): read(ver_rom.sha[1]); + fromOffset(sha2Offset): read(ver_rom.sha[2]); + fromOffset(sha3Offset): read(ver_rom.sha[3]); // Scratchpad fromOffset(scratchpadOffset): read(scratchpad); diff --git a/hdl/projects/sidecar/qsfp_x32/BUCK b/hdl/projects/sidecar/qsfp_x32/BUCK index 0b3f6caf..45743601 100644 --- a/hdl/projects/sidecar/qsfp_x32/BUCK +++ b/hdl/projects/sidecar/qsfp_x32/BUCK @@ -19,7 +19,6 @@ rdl_file( src = "qsfp_x32_controller.rdl", deps = [ ":vsc8562_rdl", - "//hdl/ip/bsv/I2C:I2CCore_rdl", ":qsfp_modules_top_rdl", ], outputs = [ diff --git a/toolchains/BUCK b/toolchains/BUCK index 0af8029b..2e596d62 100644 --- a/toolchains/BUCK +++ b/toolchains/BUCK @@ -7,7 +7,7 @@ load("vivado_toolchain.bzl", "vivado_toolchain") load("vsg_toolchain.bzl", "vsg_toolchain") load("bsv_toolchain.bzl", "bsv_toolchain") -load("yosys_toolchain.bzl", "icepack_toolchain", "nextpnr_ice40_toolchain", "nextpnr_ecp5_toolchain", "ecppack_toolchain", "yosys_toolchain") +load("yosys_toolchain.bzl", "icepack_toolchain", "nextpnr_ice40_toolchain", "nextpnr_ecp5_toolchain", "ecppack_toolchain", "icebram_toolchain", "ecpbram_toolchain", "yosys_toolchain") system_genrule_toolchain( name = "genrule", @@ -69,6 +69,16 @@ ecppack_toolchain( visibility = ["PUBLIC"], ) +icebram_toolchain( + name = "icebram", + visibility = ["PUBLIC"], +) + +ecpbram_toolchain( + name = "ecpbram", + visibility = ["PUBLIC"], +) + remote_test_execution_toolchain( name = "remote_test_execution", visibility = ["PUBLIC"], diff --git a/toolchains/yosys_toolchain.bzl b/toolchains/yosys_toolchain.bzl index 9d2f6620..58bcfaf2 100644 --- a/toolchains/yosys_toolchain.bzl +++ b/toolchains/yosys_toolchain.bzl @@ -29,6 +29,16 @@ _ecppack = select({ "config//os:windows": "ecppack.exe", }) +_icebram = select({ + "DEFAULT": "icebram", + "config//os:windows": "icebram.exe", +}) + +_ecpbram = select({ + "DEFAULT": "ecpbram", + "config//os:windows": "ecpbram.exe", +}) + def _generic_toolchain_impl(ctx): @@ -73,6 +83,22 @@ ecppack_toolchain = rule( is_toolchain_rule = True, ) +icebram_toolchain = rule( + impl = _generic_toolchain_impl, + attrs = { + "exec": attrs.string(default = _icebram), + }, + is_toolchain_rule = True, +) + +ecpbram_toolchain = rule( + impl = _generic_toolchain_impl, + attrs = { + "exec": attrs.string(default = _ecpbram), + }, + is_toolchain_rule = True, +) + yosys_toolchain = rule( impl = _generic_toolchain_impl, attrs = { diff --git a/tools/BUCK b/tools/BUCK index e1b1f27a..d742a03d 100644 --- a/tools/BUCK +++ b/tools/BUCK @@ -9,3 +9,15 @@ export_file( src = "gen_git_version_bsv_buck2.py", visibility = ["PUBLIC"], ) + +export_file( + name = "gen_version_hex", + src = "gen_version_hex.py", + visibility = ["PUBLIC"], +) + +export_file( + name = "icebram_wrapper", + src = "icebram_wrapper.py", + visibility = ["PUBLIC"], +) diff --git a/tools/bsv.bzl b/tools/bsv.bzl index cda385f5..b97d446c 100644 --- a/tools/bsv.bzl +++ b/tools/bsv.bzl @@ -543,10 +543,28 @@ def _bsv_nextpnr_ice40_bitstream_impl(ctx: AnalysisContext) -> list[Provider]: category = "nextpnr", ) + # Version stamping: patch BRAM init data with real git version + icepack_input = asc_file + if ctx.attrs.version_template_hex and ctx.attrs.version_replacement_hex: + stamped_asc = ctx.actions.declare_output("{}_stamped.asc".format(ctx.attrs.name)) + stamp_cmd = cmd_args() + stamp_cmd.add("python3", ctx.attrs._icebram_wrapper) + stamp_cmd.add(ctx.attrs._icebram[RunInfo]) + stamp_cmd.add(ctx.attrs.version_template_hex) + stamp_cmd.add(ctx.attrs.version_replacement_hex) + stamp_cmd.add(asc_file) + stamp_cmd.add(stamped_asc.as_output()) + + ctx.actions.run( + stamp_cmd, + category = "icebram", + ) + icepack_input = stamped_asc + # Run icepack to create bitstream icepack_cmd = cmd_args() icepack_cmd.add(ctx.attrs._icepack[RunInfo]) - icepack_cmd.add(asc_file) + icepack_cmd.add(icepack_input) icepack_cmd.add(bit_file.as_output()) ctx.actions.run( @@ -572,8 +590,12 @@ bsv_nextpnr_ice40_bitstream = rule( "package": attrs.string(doc = "FPGA package (e.g., 'sg48', 'ct256')"), "pinmap": attrs.source(doc = "Pin constraints file (.pcf)"), "nextpnr_args": attrs.list(attrs.string(), default = [], doc = "Additional nextpnr arguments"), + "version_template_hex": attrs.option(attrs.source(), default = None, doc = "Template hex for icebram (from pattern)"), + "version_replacement_hex": attrs.option(attrs.source(), default = None, doc = "Replacement hex for icebram (volatile, git data)"), "_nextpnr_ice40": attrs.toolchain_dep(default = "toolchains//:nextpnr-ice40"), "_icepack": attrs.toolchain_dep(default = "toolchains//:icepack"), + "_icebram": attrs.toolchain_dep(default = "toolchains//:icebram"), + "_icebram_wrapper": attrs.source(default = "//tools:icebram_wrapper"), }, ) @@ -611,10 +633,28 @@ def _bsv_nextpnr_ecp5_bitstream_impl(ctx: AnalysisContext) -> list[Provider]: category = "nextpnr_ecp5", ) + # Version stamping: patch BRAM init data with real git version + ecppack_input = config_file + if ctx.attrs.version_template_hex and ctx.attrs.version_replacement_hex: + stamped_config = ctx.actions.declare_output("{}_stamped.config".format(ctx.attrs.name)) + stamp_cmd = cmd_args() + stamp_cmd.add("python3", ctx.attrs._ecpbram_wrapper) + stamp_cmd.add(ctx.attrs._ecpbram[RunInfo]) + stamp_cmd.add(ctx.attrs.version_template_hex) + stamp_cmd.add(ctx.attrs.version_replacement_hex) + stamp_cmd.add(config_file) + stamp_cmd.add(stamped_config.as_output()) + + ctx.actions.run( + stamp_cmd, + category = "ecpbram", + ) + ecppack_input = stamped_config + # Run ecppack to create bitstream ecppack_cmd = cmd_args() ecppack_cmd.add(ctx.attrs._ecppack[RunInfo]) - ecppack_cmd.add(config_file) + ecppack_cmd.add(ecppack_input) ecppack_cmd.add(bit_file.as_output()) ctx.actions.run( @@ -640,7 +680,11 @@ bsv_nextpnr_ecp5_bitstream = rule( "package": attrs.string(doc = "FPGA package (e.g., 'CABGA381', 'CSFBGA285')"), "pinmap": attrs.source(doc = "Pin constraints file (.lpf)"), "nextpnr_args": attrs.list(attrs.string(), default = [], doc = "Additional nextpnr arguments"), + "version_template_hex": attrs.option(attrs.source(), default = None, doc = "Template hex for ecpbram (from pattern)"), + "version_replacement_hex": attrs.option(attrs.source(), default = None, doc = "Replacement hex for ecpbram (volatile, git data)"), "_nextpnr_ecp5": attrs.toolchain_dep(default = "toolchains//:nextpnr-ecp5"), "_ecppack": attrs.toolchain_dep(default = "toolchains//:ecppack"), + "_ecpbram": attrs.toolchain_dep(default = "toolchains//:ecpbram"), + "_ecpbram_wrapper": attrs.source(default = "//tools:icebram_wrapper"), }, ) diff --git a/tools/gen_version_hex.py b/tools/gen_version_hex.py new file mode 100644 index 00000000..9a021600 --- /dev/null +++ b/tools/gen_version_hex.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Generate a replacement hex file for version ROM stamping. + +Produces a 256-line hex file (one byte per line, 2 hex digits) +containing the git commit count and short SHA in big-endian byte +order. This is the "to" file for icebram/ecpbram patching. + +Layout: + [0:3] - Commit count (big-endian) + [4:7] - Short SHA (big-endian) + [8:255] - Zeros +""" + +import subprocess +import sys + + +def main(): + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + sys.exit(1) + + output_path = sys.argv[1] + + try: + sha_hex = subprocess.check_output( + ["git", "rev-parse", "HEAD"], + stderr=subprocess.DEVNULL, + text=True, + ).strip() + commit_count = int( + subprocess.check_output( + ["git", "rev-list", "--count", "HEAD"], + stderr=subprocess.DEVNULL, + text=True, + ).strip() + ) + except (subprocess.CalledProcessError, FileNotFoundError): + sha_hex = "0" * 40 + commit_count = 0 + + short_sha = int(sha_hex[:8], 16) + + lines = [] + # Bytes 0-3: commit count, big-endian + for shift in (24, 16, 8, 0): + lines.append(f"{(commit_count >> shift) & 0xFF:02X}") + # Bytes 4-7: short SHA, big-endian + for shift in (24, 16, 8, 0): + lines.append(f"{(short_sha >> shift) & 0xFF:02X}") + # Bytes 8-255: zeros + for _ in range(248): + lines.append("00") + + with open(output_path, "w") as f: + f.write("\n".join(lines) + "\n") + + +if __name__ == "__main__": + main() diff --git a/tools/icebram_wrapper.py b/tools/icebram_wrapper.py new file mode 100644 index 00000000..342e7f8a --- /dev/null +++ b/tools/icebram_wrapper.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Wrapper for icebram/ecpbram stdin/stdout interface. + +These tools read the design file from stdin and write the patched +result to stdout. This wrapper provides a file-based interface +suitable for use in build rules. + +Usage: + python3 icebram_wrapper.py + +Example: + python3 icebram_wrapper.py icebram from.hex to.hex design.asc patched.asc + python3 icebram_wrapper.py ecpbram from.hex to.hex design.config patched.config +""" + +import subprocess +import sys + + +def main(): + if len(sys.argv) != 6: + print( + f"Usage: {sys.argv[0]} ", + file=sys.stderr, + ) + sys.exit(1) + + tool, from_hex, to_hex, input_file, output_file = sys.argv[1:] + + with open(input_file, "rb") as f: + input_data = f.read() + + result = subprocess.run( + [tool, from_hex, to_hex], + input=input_data, + capture_output=True, + ) + + if result.returncode != 0: + sys.stderr.buffer.write(result.stderr) + sys.exit(result.returncode) + + with open(output_file, "wb") as f: + f.write(result.stdout) + + +if __name__ == "__main__": + main() diff --git a/tools/vivado.bzl b/tools/vivado.bzl index f53ff584..8bce1ae4 100644 --- a/tools/vivado.bzl +++ b/tools/vivado.bzl @@ -297,8 +297,11 @@ def bitstream(ctx, input_checkpoint): "max_threads": ctx.attrs.max_threads, "input_checkpoint": input_checkpoint, } + if ctx.attrs.version_replacement_hex: + out_json["version_hex"] = ctx.attrs.version_replacement_hex + vivado_flow_tcl, _ = _vivado_tcl_gen_common(ctx, flow, out_json) - + # create a bitstream file bitstream_bit = ctx.actions.declare_output("{}.bit".format(ctx.attrs.name)) bitstream_bin = ctx.actions.declare_output("{}.bin".format(ctx.attrs.name)) @@ -309,7 +312,10 @@ def bitstream(ctx, input_checkpoint): # on cache. We need this step to run if the input file content, or # constraint file content changes - vivado = _make_vivado_common(ctx, name_and_flow, vivado_flow_tcl, hidden=input_checkpoint) + hidden_deps = [input_checkpoint] + if ctx.attrs.version_replacement_hex: + hidden_deps.append(ctx.attrs.version_replacement_hex) + vivado = _make_vivado_common(ctx, name_and_flow, vivado_flow_tcl, hidden=hidden_deps) # Add output files to tclargs vivado.add("-tclargs", bitstream_bit.as_output(), @@ -367,6 +373,11 @@ vivado_bitstream = rule( "post_synth_tcl_files": attrs.list(attrs.source(doc="TCL files for project to source for things like ILA probes (post-synthesis)"), default=[]), "synth_args": attrs.list(attrs.string(), default = []), "max_threads": attrs.int(doc="Max threads for Vivado", default=8), + "version_replacement_hex": attrs.option( + attrs.source(), + default=None, + doc="Replacement hex for version stamping (volatile, git data)", + ), "_vivado_gen": attrs.exec_dep( doc="Generate a Vivado tcl for this project", default="root//tools/vivado_gen:vivado_gen", diff --git a/tools/vivado_gen/templates/bitstream.jinja2 b/tools/vivado_gen/templates/bitstream.jinja2 index c57bdf6f..45ea8f04 100644 --- a/tools/vivado_gen/templates/bitstream.jinja2 +++ b/tools/vivado_gen/templates/bitstream.jinja2 @@ -19,6 +19,40 @@ set output_bin [lindex $argv 1] set_param general.maxThreads {{project.max_threads}} open_checkpoint {{project.input_checkpoint.absolute().as_posix()}} +{% if project.version_hex %} +# Version stamping: patch the version ROM BRAM with real git data. +# Read the replacement hex file (256 lines, one byte per line). +set fp [open "{{project.version_hex.absolute().as_posix()}}" r] +set hex_data {} +while {[gets $fp line] >= 0} { + set line [string trim $line] + if {$line ne ""} { + lappend hex_data $line + } +} +close $fp + +# Find the version ROM BRAM cell by hierarchical name. +set bram_cells [get_cells -hierarchical -filter {PRIMITIVE_TYPE =~ BMEM.bram.* && NAME =~ *version_rom*}] +if {[llength $bram_cells] == 1} { + set bram [lindex $bram_cells 0] + # Build INIT_00 (256 bits = 32 bytes). Byte 0 is at bits [7:0] + # (rightmost in the hex string), byte 31 at bits [255:248]. + set init_hex "" + for {set i 31} {$i >= 0} {incr i -1} { + if {$i < [llength $hex_data]} { + append init_hex [lindex $hex_data $i] + } else { + append init_hex "00" + } + } + set_property INIT_00 256'h$init_hex $bram + puts "Version ROM stamped: INIT_00 = $init_hex" +} else { + puts "WARNING: Expected 1 version ROM BRAM, found [llength $bram_cells]" +} +{% endif %} + #write_debug_probes -force $PROJ_DIR/${PROJ_NAME}.ltx # This is a bit funky with buck2 in that this command writes out both the # .bit and the .bin. We take both names in as arguments to make buck2 happy diff --git a/tools/vivado_gen/vivado_gen.py b/tools/vivado_gen/vivado_gen.py index fc04e4cb..574df372 100644 --- a/tools/vivado_gen/vivado_gen.py +++ b/tools/vivado_gen/vivado_gen.py @@ -39,6 +39,7 @@ def __init__(self, report_file: str, max_threads: int, input_checkpoint: str, + version_hex: str = "", ): self.flow = flow self.part = part @@ -52,6 +53,7 @@ def __init__(self, self.report_file = report_file self.max_threads = max_threads self.input_checkpoint = Path(input_checkpoint) + self.version_hex = Path(version_hex) if version_hex else None @classmethod def from_dict(cls, inputs): @@ -76,7 +78,8 @@ def from_dict(cls, inputs): synth_args=inputs.get("synth_args"), report_file=inputs.get("report_file"), max_threads=inputs.get("max_threads"), - input_checkpoint=inputs.get("input_checkpoint", "") + input_checkpoint=inputs.get("input_checkpoint", ""), + version_hex=inputs.get("version_hex", ""), ) def main(): diff --git a/tools/yosys.bzl b/tools/yosys.bzl index 213b3283..345bcb95 100644 --- a/tools/yosys.bzl +++ b/tools/yosys.bzl @@ -19,7 +19,8 @@ load( def _ice40_bitstream_impl(ctx): yosys_synth_providers = yosys_vhdl_synth(ctx) next_pnr_providers = ice40_nextpnr(ctx, yosys_synth_providers) - icepack_providers = icepack(ctx, next_pnr_providers) + stamp_providers = icebram_stamp(ctx, next_pnr_providers) + icepack_providers = icepack(ctx, stamp_providers) compressed = compress_bitstream(ctx, icepack_providers) return [ DefaultInfo( @@ -30,7 +31,6 @@ def _ice40_bitstream_impl(ctx): } ) ] - pass def yosys_vhdl_synth(ctx): providers = [] @@ -119,6 +119,22 @@ def next_pnr_family_flags(family): return "--{}".format(family) +def icebram_stamp(ctx, next_pnr_providers): + """Optionally stamp version data into BRAM via icebram.""" + if ctx.attrs.version_template_hex and ctx.attrs.version_replacement_hex: + asc = next_pnr_providers[0].default_outputs[0] + stamped_asc = ctx.actions.declare_output("{}_stamped.asc".format(ctx.attrs.name)) + cmd = cmd_args() + cmd.add("python3", ctx.attrs._icebram_wrapper) + cmd.add(ctx.attrs._icebram[RunInfo]) + cmd.add(ctx.attrs.version_template_hex) + cmd.add(ctx.attrs.version_replacement_hex) + cmd.add(asc) + cmd.add(stamped_asc.as_output()) + ctx.actions.run(cmd, category="icebram") + return [DefaultInfo(default_output=stamped_asc)] + return next_pnr_providers + def icepack(ctx, next_pnr_providers): providers = [] @@ -157,13 +173,27 @@ ice40_bitstream = rule( doc="bz2 compressor", default="root//tools/bz2compress:bz2compress", ), + "version_template_hex": attrs.option( + attrs.source(), + default=None, + doc="Template hex for icebram (from pattern)", + ), + "version_replacement_hex": attrs.option( + attrs.source(), + default=None, + doc="Replacement hex for icebram (volatile, git data)", + ), "_icepack": attrs.toolchain_dep( - doc="Use system python toolchain for running python stuff", default="toolchains//:icepack", ), "_nextpnr_ice40": attrs.toolchain_dep( - doc="Use system python toolchain for running python stuff", default="toolchains//:nextpnr-ice40", ), + "_icebram": attrs.toolchain_dep( + default="toolchains//:icebram", + ), + "_icebram_wrapper": attrs.source( + default="//tools:icebram_wrapper", + ), }, ) \ No newline at end of file