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
25 changes: 25 additions & 0 deletions examples/apb/3.1_write_transfer_with_no_wait_states.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// https://support.arm.com/documentation/ihi0024/e/
// Figure 3-1 "Write transfer with no wait states" (section 3.1.1)

// Note: we assume that PENABLE is 0 during idle (i.e. before/after the write transaction),
// as Appendix A.1 of the APB spec recommends that "signals which are not required to be valid (meaningful)
// are driven to zero".

// The figure in the spec omits the following signals:
// - PSLVERR (there is no error, so it is 0 throughout)
// - PRESETn (this is a write operation, not a reset, so we set it to 1 as it is active-low)
// - PSTRB (we assume all 4 bytes of PWDATA are valid, so we set this to 0xF)
// - PPROT (we assume this is a normal data access, so we set this to 0)
// - PRDATA (this is a write operation, so read data is DontCare)

PRESETn 1 1 1 1
PSEL 0 1 1 0
PENABLE 0 0 1 0
PWRITE x 1 1 x
PADDR[31:0] x 0x1 0x1 x // "Addr 1"
PWDATA[31:0] x 0x1 0x1 x // "Data 1"
PSTRB[3:0] x 0xf 0xf x
PPROT[2:0] x 0 0 x
PRDATA[31:0] x x x x
PREADY x x 1 x
PSLVERR 0 0 0 0
28 changes: 28 additions & 0 deletions examples/apb/3.2_write_transfer_with_wait_states.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// https://support.arm.com/documentation/ihi0024/e/
// Figure 3-2 "Write transfer with wait states" (section 3.1.2)

// This is largely the same as figure 3-1, except
// the subordinate exerts backpressure for two cycles by setting PREADY to 0.

// Note: we assume that PENABLE is 0 during idle (i.e. before/after the write transaction),
// as Appendix A.1 of the APB spec recommends that "signals which are not required to be valid (meaningful)
// are driven to zero".

// The figure in the spec omits the following signals:
// - PSLVERR (there is no error, so it is 0 throughout)
// - PRESETn (this is a write operation, not a reset, so we set it to 1 as it is active-low)
// - PSTRB (we assume all 4 bytes of PWDATA are valid, so we set this to 0xF)
// - PPROT (we assume this is a normal data access, so we set this to 0)
// - PRDATA (this is a write operation, so read data is DontCare)

PRESETn 1 1 1 1 1 1
PSEL 0 1 1 1 1 0
PENABLE 0 0 1 1 1 0
PWRITE x 1 1 1 1 x
PADDR[31:0] x 0x1 0x1 0x1 0x1 x // "Addr 1"
PWDATA[31:0] x 0x1 0x1 0x1 0x1 x // "Data 1"
PSTRB[3:0] x 0xf 0xf 0xf 0xf x
PPROT[2:0] x 0 0 0 0 x
PRDATA[31:0] x x x x x x
PREADY x x 0 0 1 x
PSLVERR 0 0 0 0 0 0
25 changes: 25 additions & 0 deletions examples/apb/3.4_read_transfer_with_no_wait_states.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// https://support.arm.com/documentation/ihi0024/e/
// Figure 3-4 "Read transfer with no wait states" (section 3.3.1)

// Note: we assume that PENABLE is 0 during idle (i.e. before/after the read transaction),
// as Appendix A.1 of the APB spec recommends that "signals which are not required to be valid (meaningful)
// are driven to zero".

// The figure in the spec omits the following signals:
// - PSLVERR (there is no error, so it is 0 throughout)
// - PRESETn (this is a read operation, not a reset, so we set it to 1 as it is active-low)
// - PSTRB (section 3.2 requires all bits of PSTRB to be LOW for reads, so we set this to 0)
// - PPROT (we assume this is a normal data access, so we set this to 0)
// - PWDATA (this is a read operation, so write data is DontCare)

PRESETn 1 1 1 1
PSEL 0 1 1 0
PENABLE 0 0 1 0
PWRITE x 0 0 x
PADDR[31:0] x 0x1 0x1 x // "Addr 1"
PWDATA[31:0] x x x x
PSTRB[3:0] x 0 0 x
PPROT[2:0] x 0 0 x
PRDATA[31:0] x x 0x1 x // "Data 1"
PREADY x x 1 x
PSLVERR 0 0 0 0
25 changes: 25 additions & 0 deletions examples/apb/3.5_read_transfer_with_wait_states.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// https://support.arm.com/documentation/ihi0024/e/
// Figure 3-5 "Read transfer with wait states" (section 3.3.2)

// Note: we assume that PENABLE is 0 during idle (i.e. before/after the read transaction),
// as Appendix A.1 of the APB spec recommends that "signals which are not required to be valid (meaningful)
// are driven to zero".

// The figure in the spec omits the following signals:
// - PSLVERR (there is no error, so it is 0 throughout)
// - PRESETn (this is a read operation, not a reset, so we set it to 1 as it is active-low)
// - PSTRB (section 3.2 requires all bits of PSTRB to be LOW for reads, so we set this to 0)
// - PPROT (we assume this is a normal data access, so we set this to 0)
// - PWDATA (this is a read operation, so write data is DontCare)

PRESETn 1 1 1 1 1 1
PSEL 0 1 1 1 1 0
PENABLE 0 0 1 1 1 0
PWRITE x 0 0 0 0 x
PADDR[31:0] x 0x1 0x1 0x1 0x1 x // "Addr 1"
PWDATA[31:0] x x x x x x
PSTRB[3:0] x 0 0 0 0 x
PPROT[2:0] x 0 0 0 0 x
PRDATA[31:0] x x x x 0x1 x // "Data 1" (from cycles 1-4, PRDATA is grey in the waveform, so we assume it is DontCare for those cycles)
PREADY x x 0 0 1 x
PSLVERR 0 0 0 0 0 0
25 changes: 25 additions & 0 deletions examples/apb/3.6_example_failing_write_transfer.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// https://support.arm.com/documentation/ihi0024/e/
// Figure 3-6 "Example failing write transfer" (section 3.4.1)

// Note: we assume that PENABLE and PLSVERR are 0 during idle (i.e. before/after the write transaction),
// as Appendix A.1 of the APB spec recommends that "signals which are not required to be valid (meaningful)
// are driven to zero".


// The figure in the spec omits the following signals:
// - PRESETn (this is a write operation, not a reset, so we set it to 1 as it is active-low)
// - PSTRB (we assume all 4 bytes of PWDATA are valid, so we set this to 0xF)
// - PPROT (we assume this is a normal data access, so we set this to 0)
// - PRDATA (this is a write operation, so read data is DontCare)

PRESETn 1 1 1 1 1
PSEL 0 1 1 1 0
PENABLE 0 0 1 1 0
PWRITE x 1 1 1 x
PADDR[31:0] x 0x1 0x1 0x1 x // "Addr 1"
PWDATA[31:0] x 0x1 0x1 0x1 x // "Data 1"
PSTRB[3:0] x 0xf 0xf 0xf x
PPROT[2:0] x 0 0 0 x
PRDATA[31:0] x x x x x
PREADY x x 0 1 x
PSLVERR 0 0 0 1 0
30 changes: 30 additions & 0 deletions examples/apb/3.7_example_failing_read_transfer.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// https://support.arm.com/documentation/ihi0024/e/
// Figure 3-7 "Example failing read transfer" (section 3.4.2)

// Note: we assume that PENABLE and PLSVERR are 0 during idle (i.e. before/after the read transaction),
// as Appendix A.1 of the APB spec recommends that "signals which are not required to be valid (meaningful)
// are driven to zero"

// The figure doesn't specify a value fo PRDATA: the spec says that a read transaction
// that errors "can return invalid error" and that there is
// "no requirement for the peripheral to drive the
// data bus to all 0s for a read error", so we give `PRDATA` an arbitrary value
// 0x0badda7a in this waveform.

// The figure in the spec omits the following signals:
// - PRESETn (this is a read operation, not a reset, so we set it to 1 as it is active-low)
// - PSTRB (section 3.2 requires all bits of PSTRB to be LOW for reads, so we set this to 0)
// - PPROT (we assume this is a normal data access, so we set this to 0)
// - PWDATA (this is a read operation, so write data is DontCare)

PRESETn 1 1 1 1 1 1
PSEL 0 1 1 1 1 0
PENABLE 0 0 1 1 1 0
PWRITE x 0 0 0 0 x
PADDR[31:0] x 0x1 0x1 0x1 0x1 x // "Addr 1"
PWDATA[31:0] x x x x x x
PSTRB[3:0] x 0 0 0 0 x
PPROT[2:0] x 0 0 0 0 x
PRDATA[31:0] x x x x 0x0badda7a x // no valid read data
PREADY x x 0 0 1 x
PSLVERR 0 0 0 0 1 0
139 changes: 139 additions & 0 deletions examples/apb/apb.prot
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Key points from the APB spec:
// - "Every transfer takes at least two cycles to complete" (sec. 1.1)
// - "Data transfers cannot occur concurrently because the read data and write data buses do not have their own individual
// handshake signal" (sec. 2.1.2)

struct APB {
// active-low reset
in PRESETn: u1,

// PSEL is 1 when the subordinate is selected
// (When there are multiple subordinates, each subordinate gets its own `PSEL`
// signal to indicate if it has been selected. Here we only have one subordinate,
// so only one `PSEL` signal)
in PSEL: u1,

// Indicates the start of a data transfer (APB spec calls this the "access" state)
// (Note: per the spec, when `PENABLE` is low, `PREADY` is meaningless)
in PENABLE: u1,

// 1 = write, 0 = read
in PWRITE: u1,

// Address for reads/writes
in PADDR: u32,

// Data to be written (this is DontCare during reads)
in PWDATA: u32,

// Bitmask indicating which bytes of `PWDATA` contain valid data for writes.
// (APB spec calls this signal the "write strobe".)
// For reads, `PSTRB` is `DontCare`.
// APB spec says this signal is optional.
in PSTRB: u4,

// "Protection attributes": 3-bit signal used to indicate if the data access
// is normal/privileged/secure, and if it is accessing data/an instruction
in PPROT: u3,

// Read data (supplied by subordinate)
out PRDATA: u32,

// Ready signal issued from subordinate
out PREADY: u1,

// Optional signal indicating if an error occurred during data transfer
// Notes from APB spec section 3.4:
// - PSLVERR should be 0 when either one of PSEL, PENABLE or PREADY are low
// - PSLVERR is only meaningful when all three of PSEL, PENABLE and PREADY are high
out PSLVERR: u1,
}

#[idle]
prot idle<DUT: APB>() {
// PSEL must be 0 during idle state (sec. 4.1)
DUT.PSEL := 1'b0;

// Reset is active-low, so reset = 1 during idle (No reset)
DUT.PRESETn := 1'b1;

// Idle means we're not in the Access state, so PENABLE = 0
DUT.PENABLE := 1'b0;

DUT.PWRITE := X;
DUT.PADDR := X;
DUT.PWDATA := X;
DUT.PSTRB := X;
DUT.PPROT := X;
assert_eq(DUT.PSLVERR, 1'b0);
step();
}

prot reset<DUT: APB>() {
// Reset is active-low
DUT.PRESETn := 1'b0;

// These are 0, because during reset, we are not in the access state
// and the subordinate is not selected
DUT.PSEL := 1'b0;
DUT.PENABLE := 1'b0;
step();
}


prot write<DUT: APB>(addr: u32, data: u32, strb: u4, pprot: u3, slverr: u1) {
DUT.PRESETn := 1'b1;

// SETUP phase
DUT.PSEL := 1'b1;
DUT.PENABLE := 1'b0;
DUT.PWRITE := 1'b1;
DUT.PADDR := addr;
DUT.PWDATA := data;
DUT.PSTRB := strb;
DUT.PPROT := pprot;
assert_eq(DUT.PSLVERR, 1'b0);

step();

// ACCESS phase: hold everything, wait out the subordinate's wait states
DUT.PENABLE := 1'b1;
while !(DUT.PREADY == 1'b1) {
assert_eq(DUT.PSLVERR, 1'b0);
step();
}
assert_eq(DUT.PSLVERR, slverr);
step();
}

prot read<DUT: APB>(addr: u32, data: u32, pprot: u3, slverr: u1) {
DUT.PRESETn := 1'b1;

// SETUP phase
DUT.PSEL := 1'b1;
DUT.PENABLE := 1'b0;
DUT.PWRITE := 1'b0;
DUT.PADDR := addr;
DUT.PPROT := pprot;

// For read operations, PWDATA is meaningless
// and PSTRB must be set to all zeroes (sec. 3.2)
DUT.PWDATA := X;
DUT.PSTRB := 4'b0000;
assert_eq(DUT.PSLVERR, 1'b0);

step();

// ACCESS phase
DUT.PENABLE := 1'b1;

// Wait till subordinate becomes ready
while !(DUT.PREADY == 1'b1) {
assert_eq(DUT.PSLVERR, 1'b0);
step();
}

assert_eq(DUT.PRDATA, data);
assert_eq(DUT.PSLVERR, slverr);
step();
}
10 changes: 10 additions & 0 deletions examples/apb/ch_apb.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Arguments for read & write transactions:
// write(addr, data, strb, pprot, slverr)
// read (addr, data, pprot, slverr)
trace {
reset();
idle();
write(0x00, 0x12345678, 0xF, 0x0, 0x0);
read(0x00, 0x12345678, 0x0, 0x0);
idle();
}
14 changes: 14 additions & 0 deletions examples/apb/ch_apb_actual.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Same as ch_apb.tx, but with an extra `read` transaction in the middle to show

// Arguments for read & write transactions:
// write(addr, data, strb, pprot, slverr)
// read (addr, data, pprot, slverr)
trace {
reset();
idle();
write(0x00, 0x12345678, 0xF, 0x0, 0x0);
read(0x00, 0x00000000, 0x0, 0x0); // We add this extra "buggy" transaction here to show that `PRDATA` doesn't contain `0x12345678
read(0x00, 0x12345678, 0x0, 0x0); // Read data arrives one cycle too late
idle();
}

Binary file added examples/apb/ch_apb_actual_0.fst
Binary file not shown.
Binary file added examples/apb/ch_apb_buggy_0.fst
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// trace 0
trace {
idle(); [0]
write(0x00000001, 0x00000001, 0xf, 0x0, 0x0); [1 .. 2]
idle(); [3]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// trace 0
trace {
idle(); [0]
write(0x00000001, 0x00000001, 0xf, 0x0, 0x0); [1 .. 4]
idle(); [5]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// trace 0
trace {
idle(); [0]
read(0x00000001, 0x00000001, 0x0, 0x0); [1 .. 2]
idle(); [3]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// trace 0
trace {
idle(); [0]
read(0x00000001, 0x00000001, 0x0, 0x0); [1 .. 4]
idle(); [5]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// trace 0
trace {
idle(); [0]
write(0x00000001, 0x00000001, 0xf, 0x0, 0x1); [1 .. 3]
idle(); [4]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// trace 0
trace {
idle(); [0]
read(0x00000001, 0x0badda7a, 0x0, 0x1); [1 .. 4]
idle(); [5]
}
1 change: 1 addition & 0 deletions examples/apb/expects/ch_apb.fail.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Assertion failure in cycle 5.
9 changes: 9 additions & 0 deletions examples/apb/expects/ch_apb.interp.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error: The two expressions did not evaluate to the same value (in transaction `read(0, 305419896, 0, 0)`)
┌─ examples/apb/apb.prot:136:15
136 │ assert_eq(DUT.PRDATA, data);
│ ^^^^^^^^^^^^^^^^ LHS Value: 0, RHS Value: 305419896

Trace 0 execution failed.
---CODE---
101
6 changes: 6 additions & 0 deletions examples/apb/expects/ch_apb_0.bi.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

thread 'main' (14916314) panicked at /Users/eyn5/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wellen-0.25.6/src/viewers.rs:157:52:
failed to open input file!: Os { code: 2, kind: NotFound, message: "No such file or directory" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---CODE---
101
1 change: 1 addition & 0 deletions examples/apb/expects/ch_apb_actual.graph_interp.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Trace 0 executed successfully!
1 change: 1 addition & 0 deletions examples/apb/expects/ch_apb_actual.interp.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Trace 0 executed successfully!
4 changes: 4 additions & 0 deletions examples/apb/expects/ch_apb_actual.waveform.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---CODE---
134
---STDERR---
sh: line 1: 77452 Abort trap: 6 target/debug/graph-interp --transactions examples/apb/ch_apb_actual.tx --bound 6 --ascii-waveform --verilog examples/apb/rtl/ch_apb_slave.sv examples/apb/rtl/ch_apb_dut.v --protocol examples/apb/apb.prot --module ch_apb_dut 2> /dev/null
Loading
Loading