From 0f1f98f356eb232a609bbeb19159054ccc4a6d1d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 22 Aug 2026 08:55:15 +0000 Subject: [PATCH] Add tapeout pad sim SV models and TapeoutSimConfig Copy SimI2CEepromModel.sv and SimSPIFlashPadModel.sv from tape-env so existing SimPeripheral blackboxes resolve their /vsrc resources. Add TapeoutConfig and TapeoutSimConfig composing pad-level IO binders and harness models (no SMIC180, no Buckyball RoCC). Update README. Co-authored-by: Shiroha --- README.md | 10 +- src/main/resources/vsrc/SimI2CEepromModel.sv | 170 ++++++++++++++++++ .../resources/vsrc/SimSPIFlashPadModel.sv | 29 +++ src/main/scala/config/TapeoutConfigs.scala | 69 +++++++ 4 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/vsrc/SimI2CEepromModel.sv create mode 100644 src/main/resources/vsrc/SimSPIFlashPadModel.sv create mode 100644 src/main/scala/config/TapeoutConfigs.scala diff --git a/README.md b/README.md index 605a9aa..6b17143 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# soc-framework \ No newline at end of file +# soc-framework + +Chipyard `DigitalTop` / config / IOBinder / harness layer extracted from [Chipyard](https://github.com/ucb-bar/chipyard) for DangoSys tapeout flows. + +**TapeoutConfig** is the chip-like Rocket target: single 100 MHz clock, serial TileLink backing memory, and pad-level IO cells with a harness-attached I2C EEPROM model. + +**TapeoutSimConfig** extends that with pad-connected SPI flash simulation (`+spiflash0=`) via `WithSimSPIFlashOnPads` and `WithSimSPIIOCells`. Use it as the pad-level VCS/Verilator sim target. + +SystemVerilog pad models live in `src/main/resources/vsrc/` (`SimI2CEepromModel.sv`, `SimSPIFlashPadModel.sv`). diff --git a/src/main/resources/vsrc/SimI2CEepromModel.sv b/src/main/resources/vsrc/SimI2CEepromModel.sv new file mode 100644 index 0000000..16a9f95 --- /dev/null +++ b/src/main/resources/vsrc/SimI2CEepromModel.sv @@ -0,0 +1,170 @@ +module SimI2CEepromModel #( + parameter [6:0] I2C_ADDRESS = 7'h50 +) ( + inout scl, + inout sda, + input reset +); + + localparam [2:0] ST_IDLE = 3'd0; + localparam [2:0] ST_ADDRESS = 3'd1; + localparam [2:0] ST_ADDR_ACK = 3'd2; + localparam [2:0] ST_WRITE = 3'd3; + localparam [2:0] ST_WRITE_ACK = 3'd4; + localparam [2:0] ST_READ = 3'd5; + localparam [2:0] ST_READ_ACK = 3'd6; + + // I2C state changes on START, STOP, and SCL rising edges. These events are + // mutually exclusive protocol events, despite not sharing one clock. + /* verilator lint_off MULTIDRIVEN */ + reg [2:0] state; + reg [7:0] shift; + reg [2:0] bit_count; + reg [2:0] read_bit; + reg selected; + reg read_transfer; + reg expect_pointer; + reg sda_drive_low; + reg [7:0] memory [0:255]; + reg [7:0] memory_pointer; + /* verilator lint_on MULTIDRIVEN */ + integer index; + + pullup(scl); + pullup(sda); + assign sda = sda_drive_low ? 1'b0 : 1'bz; + + initial begin + state = ST_IDLE; + shift = 8'h00; + bit_count = 3'd0; + read_bit = 3'd0; + selected = 1'b0; + read_transfer = 1'b0; + expect_pointer = 1'b1; + sda_drive_low = 1'b0; + memory_pointer = 8'h00; + for (index = 0; index < 256; index = index + 1) begin + memory[index] = index[7:0]; + end + end + + always @(posedge reset) begin + state <= ST_IDLE; + shift <= 8'h00; + bit_count <= 3'd0; + read_bit <= 3'd0; + selected <= 1'b0; + read_transfer <= 1'b0; + expect_pointer <= 1'b1; + memory_pointer <= 8'h00; + end + + // START or repeated-START: SDA falls while SCL is released high. + always @(negedge sda) begin + if (!reset && (scl === 1'b1)) begin + state <= ST_ADDRESS; + shift <= 8'h00; + bit_count <= 3'd0; + selected <= 1'b0; + end + end + + // STOP: SDA rises while SCL is released high. + always @(posedge sda) begin + if (!reset && (scl === 1'b1)) begin + state <= ST_IDLE; + bit_count <= 3'd0; + end + end + + // The master and the EEPROM both sample input data on SCL rising edges. + always @(posedge scl) begin + if (!reset) begin + case (state) + ST_ADDRESS: begin + shift <= {shift[6:0], sda}; + if (bit_count == 3'd7) begin + selected <= (shift[6:0] == I2C_ADDRESS); + read_transfer <= sda; + bit_count <= 3'd0; + state <= ST_ADDR_ACK; + end else begin + bit_count <= bit_count + 3'd1; + end + end + + ST_ADDR_ACK: begin + if (selected) begin + if (read_transfer) begin + read_bit <= 3'd0; + state <= ST_READ; + end else begin + expect_pointer <= 1'b1; + state <= ST_WRITE; + end + end else begin + state <= ST_IDLE; + end + end + + ST_WRITE: begin + shift <= {shift[6:0], sda}; + if (bit_count == 3'd7) begin + if (expect_pointer) begin + memory_pointer <= {shift[6:0], sda}; + expect_pointer <= 1'b0; + end else begin + memory[memory_pointer] <= {shift[6:0], sda}; + memory_pointer <= memory_pointer + 8'd1; + end + bit_count <= 3'd0; + state <= ST_WRITE_ACK; + end else begin + bit_count <= bit_count + 3'd1; + end + end + + ST_WRITE_ACK: state <= ST_WRITE; + + ST_READ: begin + if (read_bit == 3'd7) begin + read_bit <= 3'd0; + state <= ST_READ_ACK; + end else begin + read_bit <= read_bit + 3'd1; + end + end + + ST_READ_ACK: begin + if (sda == 1'b0) begin + memory_pointer <= memory_pointer + 8'd1; + read_bit <= 3'd0; + state <= ST_READ; + end else begin + state <= ST_IDLE; + end + end + + default: begin + end + endcase + end + end + + // The EEPROM drives ACK and read data while SCL is low, before the next + // sampling edge. It only ever drives zero, preserving I2C open-drain rules. + always @(posedge reset or negedge scl) begin + if (reset) begin + sda_drive_low <= 1'b0; + end else begin + case (state) + ST_ADDR_ACK: sda_drive_low <= selected; + ST_WRITE_ACK: sda_drive_low <= selected; + ST_READ: sda_drive_low <= ~memory[memory_pointer][7-read_bit]; + default: sda_drive_low <= 1'b0; + endcase + end + end + +endmodule diff --git a/src/main/resources/vsrc/SimSPIFlashPadModel.sv b/src/main/resources/vsrc/SimSPIFlashPadModel.sv new file mode 100644 index 0000000..fa60592 --- /dev/null +++ b/src/main/resources/vsrc/SimSPIFlashPadModel.sv @@ -0,0 +1,29 @@ +module SimSPIFlashPadModel #( + parameter string PLUSARG, + parameter bit READONLY, + parameter longint CAPACITY_BYTES +) ( + inout sck, + inout cs, + inout dq_0, + inout dq_1, + inout dq_2, + inout dq_3, + input reset +); + + SimSPIFlashModel #( + .PLUSARG(PLUSARG), + .READONLY(READONLY), + .CAPACITY_BYTES(CAPACITY_BYTES) + ) flash ( + .sck(sck), + .cs_0(cs), + .dq_0(dq_0), + .dq_1(dq_1), + .dq_2(dq_2), + .dq_3(dq_3), + .reset(reset) + ); + +endmodule diff --git a/src/main/scala/config/TapeoutConfigs.scala b/src/main/scala/config/TapeoutConfigs.scala new file mode 100644 index 0000000..7895c2b --- /dev/null +++ b/src/main/scala/config/TapeoutConfigs.scala @@ -0,0 +1,69 @@ +package chipyard + +import org.chipsalliance.cde.config.Config +import testchipip.soc.MBUS + +/** + * Tapeout target based on the current chip-like Rocket configuration. + * + * Uses generic IO cells (inherited from AbstractConfig) and attaches a + * behavioral I2C EEPROM at the physical pads in simulation. + */ +class TapeoutConfig extends Config( + new freechips.rocketchip.subsystem.WithoutTLMonitors ++ + new WithTapeoutRocket ++ + new testchipip.soc.WithNoScratchpads ++ + new chipyard.clocking.WithNdmResetInSystemReset ++ + new WithTapeoutSingleClock(100) ++ + new chipyard.harness.WithSimTSIOverSerialTL(fast = true) ++ + new chipyard.harness.WithSimI2CEepromOnPads ++ + new WithSerialConnect ++ + new chipyard.iobinders.WithSPIIOCells ++ + new chipyard.iobinders.WithSimI2CIOCells ++ + new chipyard.config.WithUART( + baudrate = 115200, + address = 0x10020000, + txEntries = 8, + rxEntries = 8) ++ + new chipyard.config.WithNoUART ++ + new chipyard.config.WithSPI(address = 0x10031000) ++ + new chipyard.config.WithI2C(address = 0x10040000) ++ + new chipyard.config.WithGPIO(address = 0x10010000, width = 8) ++ + new chipyard.config.AbstractConfig) + +/** + * TapeoutConfig with pad-connected SPI flash and I2C EEPROM simulation models. + * SPI flash requires +spiflash0=; the I2C EEPROM is preloaded with byte[i] = i. + */ +class TapeoutSimConfig extends Config( + new chipyard.harness.WithSimSPIFlashOnPads ++ + new chipyard.iobinders.WithSimSPIIOCells ++ + new TapeoutConfig) + +class WithTapeoutRocket extends Config( + new freechips.rocketchip.rocket.WithL1ICacheSets(64) ++ + new freechips.rocketchip.rocket.WithL1ICacheWays(1) ++ + new freechips.rocketchip.rocket.WithL1DCacheSets(64) ++ + new freechips.rocketchip.rocket.WithL1DCacheWays(1) ++ + new freechips.rocketchip.subsystem.WithInclusiveCacheDirReg(true) ++ + new freechips.rocketchip.subsystem.WithInclusiveCacheSchedulerBypass(false) ++ + new freechips.rocketchip.subsystem.WithInclusiveCache(nWays = 8, capacityKB = 16) ++ + new freechips.rocketchip.rocket.WithNHugeCores(1) +) + +class WithSerialConnect extends Config( + new testchipip.serdes.WithSerialTLMem(size = BigInt("10000000", 16)) ++ + new testchipip.serdes.WithSerialTLPHYParams( + testchipip.serdes.DecoupledExternalSyncSerialPhyParams(phitWidth = 4, flitWidth = 16)) ++ + new chipyard.config.WithSerialTLBackingMemory ++ + new testchipip.soc.WithOffchipBusClient(MBUS) ++ + new testchipip.soc.WithOffchipBus +) + +class WithTapeoutSingleClock(freqMHz: Int) extends Config( + new chipyard.clocking.WithSingleClockBroadcastClockGenerator(freqMHz) ++ + new chipyard.config.WithTileFrequency(freqMHz.toDouble) ++ + new chipyard.config.WithUniformBusFrequencies(freqMHz.toDouble) ++ + new chipyard.harness.WithHarnessBinderClockFreqMHz(freqMHz.toDouble) ++ + new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator +)