Skip to content
Draft
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
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# soc-framework
# 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`).
170 changes: 170 additions & 0 deletions src/main/resources/vsrc/SimI2CEepromModel.sv
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions src/main/resources/vsrc/SimSPIFlashPadModel.sv
Original file line number Diff line number Diff line change
@@ -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
69 changes: 69 additions & 0 deletions src/main/scala/config/TapeoutConfigs.scala
Original file line number Diff line number Diff line change
@@ -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
)