diff --git a/my_design/design.py b/my_design/design.py index 01e6953..54881fd 100644 --- a/my_design/design.py +++ b/my_design/design.py @@ -13,11 +13,11 @@ from amaranth_orchard.memory import SRAMPeripheral from amaranth_orchard.io import GPIOPeripheral from amaranth_orchard.io import UARTPeripheral +from amaranth_orchard.io import SPISignature, SPIPeripheral +from amaranth_orchard.io import I2CSignature, I2CPeripheral from amaranth_cv32e40p.cv32e40p import CV32E40P, DebugModule from chipflow_lib.platforms import InputPinSignature, OutputPinSignature -from .ips.spi import SPISignature, SPIPeripheral -from .ips.i2c import I2CSignature, I2CPeripheral from .ips.pwm import PWMPins, PWMPeripheral # from .ips.pdm import PDMPeripheral diff --git a/my_design/ips/glasgow_i2c.py b/my_design/ips/glasgow_i2c.py deleted file mode 100644 index c1419ef..0000000 --- a/my_design/ips/glasgow_i2c.py +++ /dev/null @@ -1,246 +0,0 @@ -from amaranth import * -from amaranth.lib.cdc import FFSynchronizer - - -# I2C Controller Implementation from Glasgow -# https://github.com/GlasgowEmbedded/glasgow/blob/5e1f94dde6896e3adf2e8ebcfc0157d1fb950823/software/glasgow/gateware/i2c.py - -class I2CBus(Elaboratable): - """ - I2C bus. - - Decodes bus conditions (start, stop, sample and setup) and provides synchronization. - """ - def __init__(self, pins): - - self.pins = pins - - self.scl_i = Signal() - self.scl_o = Signal(init=1) - self.sda_i = Signal() - self.sda_o = Signal(init=1) - - self.sample = Signal(name="bus_sample") - self.setup = Signal(name="bus_setup") - self.start = Signal(name="bus_start") - self.stop = Signal(name="bus_stop") - - def elaborate(self, platform): - m = Module() - - scl_r = Signal(init=1) - sda_r = Signal(init=1) - - m.d.comb += [ - self.pins.scl.o.eq(0), - self.pins.scl.oe.eq(~self.scl_o), - self.pins.sda.o.eq(0), - self.pins.sda.oe.eq(~self.sda_o), - - self.sample.eq(~scl_r & self.scl_i), - self.setup.eq(scl_r & ~self.scl_i), - self.start.eq(self.scl_i & sda_r & ~self.sda_i), - self.stop.eq(self.scl_i & ~sda_r & self.sda_i), - ] - m.d.sync += [ - scl_r.eq(self.scl_i), - sda_r.eq(self.sda_i), - ] - m.submodules += [ - FFSynchronizer(self.pins.scl.i, self.scl_i, init=1), - FFSynchronizer(self.pins.sda.i, self.sda_i, init=1), - ] - - return m - - -class I2CInitiator(Elaboratable): - """ - Simple I2C transaction initiator. - - Generates start and stop conditions, and transmits and receives octets. - Clock stretching is supported. - - :param period_cyc: - Bus clock period, as a multiple of system clock period. - :type period_cyc: int - :param clk_stretch: - If true, SCL will be monitored for devices stretching the clock. Otherwise, - only internally generated SCL is considered. - :type clk_stretch: bool - - :attr busy: - Busy flag. Low if the state machine is idle, high otherwise. - :attr start: - Start strobe. When ``busy`` is low, asserting ``start`` for one cycle generates - a start or repeated start condition on the bus. Ignored when ``busy`` is high. - :attr stop: - Stop strobe. When ``busy`` is low, asserting ``stop`` for one cycle generates - a stop condition on the bus. Ignored when ``busy`` is high. - :attr write: - Write strobe. When ``busy`` is low, asserting ``write`` for one cycle receives - an octet on the bus and latches it to ``data_o``, after which the acknowledge bit - is asserted if ``ack_i`` is high. Ignored when ``busy`` is high. - :attr data_i: - Data octet to be transmitted. Latched immediately after ``write`` is asserted. - :attr ack_o: - Received acknowledge bit. - :attr read: - Read strobe. When ``busy`` is low, asserting ``read`` for one cycle latches - ``data_i`` and transmits it on the bus, after which the acknowledge bit - from the bus is latched to ``ack_o``. Ignored when ``busy`` is high. - :attr data_o: - Received data octet. - :attr ack_i: - Acknowledge bit to be transmitted. Latched immediately after ``read`` is asserted. - """ - def __init__(self, pins, clk_stretch=True): - self.clk_stretch = clk_stretch - - self.period_cyc = Signal(12) - - self.busy = Signal(init=1) - self.start = Signal() - self.stop = Signal() - self.read = Signal() - self.data_i = Signal(8) - self.ack_o = Signal() - self.write = Signal() - self.data_o = Signal(8) - self.ack_i = Signal() - - self.bus = I2CBus(pins) - - def elaborate(self, platform): - m = Module() - - m.submodules.bus = self.bus - - timer = Signal(12) - stb = Signal() - - with m.If((timer == 0) | ~self.busy): - m.d.sync += timer.eq(self.period_cyc) - with m.Elif((not self.clk_stretch) | (self.bus.scl_o == self.bus.scl_i)): - m.d.sync += timer.eq(timer - 1) - m.d.comb += stb.eq(timer == 0) - - bitno = Signal(range(8)) - r_shreg = Signal(8) - w_shreg = Signal(8) - r_ack = Signal() - - with m.FSM() as fsm: - self._fsm = fsm - def scl_l(state, next_state, *exprs): - with m.State(state): - with m.If(stb): - m.d.sync += self.bus.scl_o.eq(0) - m.next = next_state - m.d.sync += exprs - - def scl_h(state, next_state, *exprs): - with m.State(state): - with m.If(stb): - m.d.sync += self.bus.scl_o.eq(1) - with m.Elif(self.bus.scl_o == 1): - with m.If((not self.clk_stretch) | (self.bus.scl_i == 1)): - m.next = next_state - m.d.sync += exprs - - def stb_x(state, next_state, *exprs, bit7_next_state=None): - with m.State(state): - with m.If(stb): - m.next = next_state - if bit7_next_state is not None: - with m.If(bitno == 7): - m.next = bit7_next_state - m.d.sync += exprs - - with m.State("IDLE"): - m.d.sync += self.busy.eq(1) - with m.If(self.start): - with m.If(self.bus.scl_i & self.bus.sda_i): - m.next = "START-SDA-L" - with m.Elif(~self.bus.scl_i): - m.next = "START-SCL-H" - with m.Elif(self.bus.scl_i): - m.next = "START-SCL-L" - with m.Elif(self.stop): - with m.If(self.bus.scl_i & ~self.bus.sda_o): - m.next = "STOP-SDA-H" - with m.Elif(~self.bus.scl_i): - m.next = "STOP-SCL-H" - with m.Elif(self.bus.scl_i): - m.next = "STOP-SCL-L" - with m.Elif(self.write): - m.d.sync += w_shreg.eq(self.data_i) - m.next = "WRITE-DATA-SCL-L" - with m.Elif(self.read): - m.d.sync += r_ack.eq(self.ack_i) - m.next = "READ-DATA-SCL-L" - with m.Else(): - m.d.sync += self.busy.eq(0) - - # start - scl_l("START-SCL-L", "START-SDA-H") - stb_x("START-SDA-H", "START-SCL-H", - self.bus.sda_o.eq(1) - ) - scl_h("START-SCL-H", "START-SDA-L") - stb_x("START-SDA-L", "IDLE", - self.bus.sda_o.eq(0) - ) - # stop - scl_l("STOP-SCL-L", "STOP-SDA-L") - stb_x("STOP-SDA-L", "STOP-SCL-H", - self.bus.sda_o.eq(0) - ) - scl_h("STOP-SCL-H", "STOP-SDA-H") - stb_x("STOP-SDA-H", "IDLE", - self.bus.sda_o.eq(1) - ) - # write data - scl_l("WRITE-DATA-SCL-L", "WRITE-DATA-SDA-X") - stb_x("WRITE-DATA-SDA-X", "WRITE-DATA-SCL-H", - self.bus.sda_o.eq(w_shreg[7]) - ) - scl_h("WRITE-DATA-SCL-H", "WRITE-DATA-SDA-N", - w_shreg.eq(Cat(C(0, 1), w_shreg[0:7])) - ) - stb_x("WRITE-DATA-SDA-N", "WRITE-DATA-SCL-L", - bitno.eq(bitno + 1), - bit7_next_state="WRITE-ACK-SCL-L" - ) - # write ack - scl_l("WRITE-ACK-SCL-L", "WRITE-ACK-SDA-H") - stb_x("WRITE-ACK-SDA-H", "WRITE-ACK-SCL-H", - self.bus.sda_o.eq(1) - ) - scl_h("WRITE-ACK-SCL-H", "WRITE-ACK-SDA-N", - self.ack_o.eq(~self.bus.sda_i) - ) - stb_x("WRITE-ACK-SDA-N", "IDLE") - # read data - scl_l("READ-DATA-SCL-L", "READ-DATA-SDA-H") - stb_x("READ-DATA-SDA-H", "READ-DATA-SCL-H", - self.bus.sda_o.eq(1) - ) - scl_h("READ-DATA-SCL-H", "READ-DATA-SDA-N", - r_shreg.eq(Cat(self.bus.sda_i, r_shreg[0:7])) - ) - stb_x("READ-DATA-SDA-N", "READ-DATA-SCL-L", - bitno.eq(bitno + 1), - bit7_next_state="READ-ACK-SCL-L" - ) - # read ack - scl_l("READ-ACK-SCL-L", "READ-ACK-SDA-X") - stb_x("READ-ACK-SDA-X", "READ-ACK-SCL-H", - self.bus.sda_o.eq(~r_ack) - ) - scl_h("READ-ACK-SCL-H", "READ-ACK-SDA-N", - self.data_o.eq(r_shreg) - ) - stb_x("READ-ACK-SDA-N", "IDLE") - - return m diff --git a/my_design/ips/i2c.py b/my_design/ips/i2c.py deleted file mode 100644 index 2ff2e4a..0000000 --- a/my_design/ips/i2c.py +++ /dev/null @@ -1,107 +0,0 @@ -from amaranth import * -from amaranth.lib import wiring -from amaranth.lib.wiring import In, Out, connect, flipped -from amaranth.lib.cdc import FFSynchronizer - -from amaranth_soc import csr -from chipflow_lib.platforms import BidirPinSignature -from .glasgow_i2c import I2CInitiator - -__all__ = ["I2CPeripheral", "I2CSignature"] - -I2CSignature = wiring.Signature({ - "scl": Out(BidirPinSignature(1)), - "sda": Out(BidirPinSignature(1)) - }) - - -class I2CPeripheral(wiring.Component): - class Divider(csr.Register, access="rw"): - """I2C SCK clock divider, 1 = divide by 4""" - val: csr.Field(csr.action.RW, unsigned(12)) - - class Action(csr.Register, access="w"): - """ - reset: reset the core, e.g. in case of a bus lockup - start: write 1 to trigger I2C start - stop: write 1 to trigger I2C stop - read_ack: write 1 to trigger I2C read and ACK - read_nack: write 1 to trigger I2C read and NACK - """ - reset: csr.Field(csr.action.W, unsigned(1)) - start: csr.Field(csr.action.W, unsigned(1)) - stop: csr.Field(csr.action.W, unsigned(1)) - read_ack: csr.Field(csr.action.W, unsigned(1)) - read_nack: csr.Field(csr.action.W, unsigned(1)) - - - class SendData(csr.Register, access="w"): - """writes the given data onto the I2C bus when written to""" - val: csr.Field(csr.action.W, unsigned(8)) - - class ReceiveData(csr.Register, access="r"): - """data received from the last read""" - val: csr.Field(csr.action.R, unsigned(8)) - - class Status(csr.Register, access="r"): - busy: csr.Field(csr.action.R, unsigned(1)) - ack: csr.Field(csr.action.R, unsigned(1)) - - """ - A minimal I2C controller wrapping the Glasgow core - """ - def __init__(self): - regs = csr.Builder(addr_width=5, data_width=8) - - self._divider = regs.add("divider", self.Divider(), offset=0x00) - self._action = regs.add("action", self.Action(), offset=0x04) - self._send_data = regs.add("send_data", self.SendData(), offset=0x08) - self._receive_data = regs.add("receive_data", self.ReceiveData(), offset=0x0C) - self._status = regs.add("status", self.Status(), offset=0x10) - - self._bridge = csr.Bridge(regs.as_memory_map()) - - super().__init__({ - "i2c_pins": Out(I2CSignature), - "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), - }) - self.bus.memory_map = self._bridge.bus.memory_map - - def elaborate(self, platform): - m = Module() - - m.submodules.bridge = self._bridge - - connect(m, flipped(self.bus), self._bridge.bus) - - i2c_rst = Signal() - - m.submodules.i2c = i2c = ResetInserter(i2c_rst)(I2CInitiator(self.i2c_pins)) - - m.d.comb += [ - i2c.period_cyc.eq(self._divider.f.val.data), - - i2c_rst.eq(self._action.f.reset.w_data & self._action.f.reset.w_stb), - i2c.start.eq(self._action.f.start.w_data & self._action.f.start.w_stb), - i2c.stop.eq(self._action.f.stop.w_data & self._action.f.stop.w_stb), - i2c.read.eq((self._action.f.read_ack.w_data & self._action.f.read_ack.w_stb) | \ - (self._action.f.read_nack.w_data & self._action.f.read_nack.w_stb)), - i2c.ack_i.eq(self._action.f.read_ack.w_data), - - i2c.data_i.eq(self._send_data.f.val.w_data), - i2c.write.eq(self._send_data.f.val.w_stb), - - self._receive_data.f.val.r_data.eq(i2c.data_o), - - self._status.f.busy.r_data.eq(i2c.busy), - self._status.f.ack.r_data.eq(i2c.ack_o), - ] - return m - -if __name__ == '__main__': - from amaranth.back import verilog - from pathlib import Path - i2c = I2CPeripheral(name="i2c") - Path("build/export/ips").mkdir(parents=True, exist_ok=True) - with open("build/export/ips/i2c.v", "w") as f: - f.write(verilog.convert(i2c, name="i2c_peripheral")) diff --git a/my_design/ips/spi.py b/my_design/ips/spi.py deleted file mode 100644 index aa37c1a..0000000 --- a/my_design/ips/spi.py +++ /dev/null @@ -1,202 +0,0 @@ -from amaranth import Module, Signal, Cat, C, unsigned -from amaranth.lib import wiring -from amaranth.lib.wiring import In, Out, connect, flipped - -from amaranth_soc import csr -from chipflow_lib.platforms import InputPinSignature, OutputPinSignature - - - -__all__ = ["SPISignature", "SPISignature"] - - -SPISignature = wiring.Signature({ - "sck": Out(OutputPinSignature(1)), - "copi": Out(OutputPinSignature(1)), - "cipo": Out(InputPinSignature(1)), - "csn": Out(OutputPinSignature(1)), -}) - - -class SPIController(wiring.Component): - def __init__(self): - super().__init__({ - "spi": Out(SPISignature), - "sck_idle": In(1), - "sck_edge": In(1), - "cs": In(1), - "start_xfer": In(1), - "width": In(5), - "divider": In(8), - "d_send": In(32), - "d_recv": Out(32), - "busy": Out(1), - "done": Out(1), - }) - - def elaborate(self, platform): - m = Module() - sck = Signal() - - setup = Signal() - latch = Signal() - - div_ctr = Signal(8) - sr_o = Signal(32) - sr_i = Signal(32) - bit_count = Signal(6) - - m.d.comb += [ - self.spi.sck.o.eq(sck ^ self.sck_idle), - self.spi.csn.o.eq(~self.cs), - ] - - # defaults for strobes - m.d.sync += [ - self.done.eq(0), - ] - - with m.FSM(): - with m.State("IDLE"): - with m.If(self.start_xfer): - m.next = "ACTIVE" - m.d.sync += [ - sr_i.eq(0), - sr_o.eq(self.d_send), - ] - m.d.sync += [ - bit_count.eq(0), - div_ctr.eq(0), - sck.eq(0), - ] - with m.State("ACTIVE"): - with m.If(div_ctr == self.divider): - with m.If(sck): - # second half phase, SCK about to fall - with m.If(bit_count == self.width): - m.next = "DONE" - with m.Else(): - m.d.sync += bit_count.eq(bit_count + 1) - m.d.comb += [ - setup.eq(self.sck_edge), - latch.eq(~self.sck_edge), - ] - with m.Else(): - # first half phase, SCK about to rise - m.d.comb += [ - setup.eq(~self.sck_edge), - latch.eq(self.sck_edge), - ] - m.d.sync += [ - sck.eq(~sck), - div_ctr.eq(0), - ] - with m.Else(): - m.d.sync += div_ctr.eq(div_ctr + 1) - m.d.comb += self.busy.eq(1) - with m.State("DONE"): - # one extra clock cycle of busy, because on some phases it might take that to latch - m.d.comb += self.busy.eq(1) - m.d.sync += self.done.eq(1) - m.next = "IDLE" - - # shift registers - with m.If(setup): - m.d.sync += sr_o.eq(Cat(C(0, 1), sr_o)) - with m.If(latch): - m.d.sync += sr_i.eq(Cat(self.spi.cipo.i, sr_i)) - - m.d.comb += [ - self.d_recv.eq(sr_i), - self.spi.copi.o.eq(sr_o[-1]) - ] - - return m - - -class SPIPeripheral(wiring.Component): - class Config(csr.Register, access="rw"): - """ - sck_idle: idle state of sck, '1' to invert sck - sck_edge: - 1 to latch output on rising sck edge, read input on falling sck edge - 0 to read input on rising sck edge, latch output on falling sck edge - chip_select: write '1' to assert (bring low) chip select output - width: width of transfer, minus 1 - """ - sck_idle: csr.Field(csr.action.RW, unsigned(1)) - sck_edge: csr.Field(csr.action.RW, unsigned(1)) - chip_select: csr.Field(csr.action.RW, unsigned(1)) - width: csr.Field(csr.action.RW, unsigned(5)) - - class Divider(csr.Register, access="rw"): - """SPI SCK clock divider, 1 = divide by 4""" - val: csr.Field(csr.action.RW, unsigned(8)) - - class SendData(csr.Register, access="w"): - """data to transmit, must be left justified (bits [31..32-N] used)""" - val: csr.Field(csr.action.W, unsigned(32)) - - class ReceiveData(csr.Register, access="r"): - """data received, is right justified (bits [N-1..0] used)""" - val: csr.Field(csr.action.R, unsigned(32)) - - class Status(csr.Register, access="r"): - """recv_full is 1 when transfer has been completed. reset to zero by reading receive_data""" - recv_full: csr.Field(csr.action.R, unsigned(1)) - - - """ - A custom, minimal SPI controller - """ - def __init__(self): - regs = csr.Builder(addr_width=5, data_width=8) - - self._config = regs.add("config", self.Config(), offset=0x00) - self._divider = regs.add("divider", self.Divider(), offset=0x04) - self._send_data = regs.add("send_data", self.SendData(), offset=0x08) - self._receive_data = regs.add("receive_data", self.ReceiveData(), offset=0x0C) - self._status = regs.add("status", self.Status(), offset=0x10) - - self._bridge = csr.Bridge(regs.as_memory_map()) - - super().__init__({ - "spi_pins": Out(SPISignature), - "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), - }) - self.bus.memory_map = self._bridge.bus.memory_map - - def elaborate(self, platform): - m = Module() - m.submodules.bridge = self._bridge - - connect(m, flipped(self.bus), self._bridge.bus) - - m.submodules.spi = spi = SPIController() - connect(m, flipped(self.spi_pins), spi.spi) - - with m.If(self._receive_data.f.val.r_stb): - m.d.sync += self._status.f.recv_full.r_data.eq(0) - with m.Elif(spi.done): - m.d.sync += self._status.f.recv_full.r_data.eq(1) - - m.d.comb += [ - spi.sck_idle.eq(self._config.f.sck_idle.data), - spi.sck_edge.eq(self._config.f.sck_edge.data), - spi.cs.eq(self._config.f.chip_select.data), - spi.width.eq(self._config.f.width.data), - spi.divider.eq(self._divider.f.val.data), - spi.start_xfer.eq(self._send_data.f.val.w_stb), - spi.d_send.eq(self._send_data.f.val.w_data), - self._receive_data.f.val.r_data.eq(spi.d_recv), - ] - - return m - -if __name__ == '__main__': - from amaranth.back import verilog - from pathlib import Path - spi = SPIPeripheral(name="spi") - Path("build/export/ips").mkdir(parents=True, exist_ok=True) - with open("build/export/ips/spi_peripheral.v", "w") as f: - f.write(verilog.convert(spi, name="spi_peripheral")) diff --git a/my_design/ips/test_i2c.py b/my_design/ips/test_i2c.py deleted file mode 100644 index be3323d..0000000 --- a/my_design/ips/test_i2c.py +++ /dev/null @@ -1,145 +0,0 @@ -from amaranth import * -from amaranth.sim import Simulator, Tick - -from my_design.ips.i2c import I2CPeripheral -import unittest - -class _I2CHarness(Elaboratable): - def __init__(self): - self.i2c = I2CPeripheral() - self.sda = Signal() - self.scl = Signal() - self.sda_i = Signal(init=1) - self.scl_i = Signal(init=1) - def elaborate(self, platform): - m = Module() - m.submodules.i2c = self.i2c - # Simulate the open-drain I2C bus - m.d.comb += [ - self.sda.eq(~self.i2c.i2c_pins.sda.oe & self.sda_i), - self.scl.eq(~self.i2c.i2c_pins.scl.oe & self.scl_i), - - self.i2c.i2c_pins.sda.i.eq(self.sda), - self.i2c.i2c_pins.scl.i.eq(self.scl), - ] - return m - -class TestI2CPeripheral(unittest.TestCase): - - REG_DIVIDER = 0x00 - REG_ACTION = 0x04 - REG_SEND_DATA = 0x08 - REG_RECEIVE_DATA = 0x0C - REG_STATUS = 0x10 - - def _write_reg(self, dut, reg, value, width=4): - for i in range(width): - yield dut.bus.addr.eq(reg + i) - yield dut.bus.w_data.eq((value >> (8 * i)) & 0xFF) - yield dut.bus.w_stb.eq(1) - yield Tick() - yield dut.bus.w_stb.eq(0) - - def _check_reg(self, dut, reg, value, width=4): - result = 0 - for i in range(width): - yield dut.bus.addr.eq(reg + i) - yield dut.bus.r_stb.eq(1) - yield Tick() - result |= (yield dut.bus.r_data) << (8 * i) - yield dut.bus.r_stb.eq(0) - self.assertEqual(result, value) - - def test_start_stop(self): - """Test I2C start and stop conditions""" - dut = _I2CHarness() - def testbench(): - yield from self._write_reg(dut.i2c, self.REG_DIVIDER, 1, 4) - yield Tick() - yield from self._write_reg(dut.i2c, self.REG_ACTION, 1<<1, 1) # START - yield Tick() - yield from self._check_reg(dut.i2c, self.REG_STATUS, 1, 1) # busy - self.assertEqual((yield dut.sda), 1) - self.assertEqual((yield dut.scl), 1) - yield Tick() - self.assertEqual((yield dut.sda), 0) - self.assertEqual((yield dut.scl), 1) - yield Tick() - yield from self._check_reg(dut.i2c, self.REG_STATUS, 0, 1) # not busy - yield from self._write_reg(dut.i2c, self.REG_ACTION, 1<<2, 1) # STOP - for i in range(3): yield Tick() - self.assertEqual((yield dut.sda), 1) - self.assertEqual((yield dut.scl), 1) - yield Tick() - yield from self._check_reg(dut.i2c, self.REG_STATUS, 0, 1) # not busy - - sim = Simulator(dut) - sim.add_clock(1e-5) - sim.add_testbench(testbench) - with sim.write_vcd("i2c_start_test.vcd", "i2c_start_test.gtkw"): - sim.run() - - def test_write(self): - dut = _I2CHarness() - def testbench(): - yield from self._write_reg(dut.i2c, self.REG_DIVIDER, 1, 4) - yield Tick() - yield from self._write_reg(dut.i2c, self.REG_ACTION, 1<<1, 1) # START - for i in range(10): yield Tick() # wait for START to be completed - for data in (0xAB, 0x63): - yield from self._write_reg(dut.i2c, self.REG_SEND_DATA, data, 1) # write - for i in range(3): yield Tick() - for bit in reversed(range(-1, 8)): - self.assertEqual((yield dut.scl), 0) - for i in range(4): yield Tick() - if bit == -1: # ack - yield (dut.sda_i.eq(0)) - else: - self.assertEqual((yield dut.sda), (data >> bit) & 0x1) - for i in range(2): yield Tick() - self.assertEqual((yield dut.scl), 1) - for i in range(6): yield Tick() - yield (dut.sda_i.eq(1)) # reset bus - for i in range(20): yield Tick() - yield from self._check_reg(dut.i2c, self.REG_STATUS, 2, 1) # not busy, acked - sim = Simulator(dut) - sim.add_clock(1e-5) - sim.add_testbench(testbench) - with sim.write_vcd("i2c_write_test.vcd", "i2c_write_test.gtkw"): - sim.run() - - def test_read(self): - dut = _I2CHarness() - data = 0xA3 - def testbench(): - yield from self._write_reg(dut.i2c, self.REG_DIVIDER, 1, 4) - yield Tick() - yield from self._write_reg(dut.i2c, self.REG_ACTION, 1<<1, 1) # START - for i in range(10): yield Tick() # wait for START to be completed - yield from self._write_reg(dut.i2c, self.REG_ACTION, 1<<3, 1) # READ, ACK - for i in range(3): yield Tick() - for bit in reversed(range(-1, 8)): - self.assertEqual((yield dut.scl), 0) - for i in range(4): yield Tick() - if bit == -1: # ack - self.assertEqual((yield dut.sda), 0) - else: - yield (dut.sda_i.eq((data >> bit) & 0x1)) - for i in range(2): yield Tick() - self.assertEqual((yield dut.scl), 1) - for i in range(6): yield Tick() - if bit == 0: - yield (dut.sda_i.eq(1)) # reset bus - - for i in range(20): yield Tick() - yield from self._check_reg(dut.i2c, self.REG_STATUS, 0, 1) # not busy - yield from self._check_reg(dut.i2c, self.REG_RECEIVE_DATA, data, 1) # data - sim = Simulator(dut) - sim.add_clock(1e-5) - sim.add_testbench(testbench) - with sim.write_vcd("i2c_read_test.vcd", "i2c_read_test.gtkw"): - sim.run() - -if __name__ == "__main__": - unittest.main() - diff --git a/my_design/ips/test_spi.py b/my_design/ips/test_spi.py deleted file mode 100644 index 30b7fac..0000000 --- a/my_design/ips/test_spi.py +++ /dev/null @@ -1,125 +0,0 @@ -from amaranth import * -from amaranth.sim import Simulator, Tick - -from my_design.ips.spi import SPIPeripheral, SPISignature -import unittest - -class TestSpiPeripheral(unittest.TestCase): - - REG_CONFIG = 0x00 - REG_DIV = 0x04 - REG_SEND_DATA = 0x08 - REG_RECEIVE_DATA = 0x0C - REG_STATUS = 0x10 - - def _write_reg(self, dut, reg, value, width=4): - for i in range(width): - yield dut.bus.addr.eq(reg + i) - yield dut.bus.w_data.eq((value >> (8 * i)) & 0xFF) - yield dut.bus.w_stb.eq(1) - yield Tick() - yield dut.bus.w_stb.eq(0) - - def _check_reg(self, dut, reg, value, width=4): - result = 0 - for i in range(width): - yield dut.bus.addr.eq(reg + i) - yield dut.bus.r_stb.eq(1) - yield Tick() - result |= (yield dut.bus.r_data) << (8 * i) - yield dut.bus.r_stb.eq(0) - self.assertEqual(result, value) - - def test_chip_select(self): - dut = SPIPeripheral() - def testbench(): - yield from self._write_reg(dut, self.REG_CONFIG, 1<<2, 1) - yield Tick() - self.assertEqual((yield dut.spi_pins.csn.o), 0) - yield from self._write_reg(dut, self.REG_CONFIG, 0<<2, 1) - yield Tick() - self.assertEqual((yield dut.spi_pins.csn.o), 1) - - sim = Simulator(dut) - sim.add_clock(1e-5) - sim.add_testbench(testbench) - with sim.write_vcd("spi_cs_test.vcd", "spi_cs_test.gtkw"): - sim.run() - - def test_xfer(self): - dut = SPIPeripheral() - tests = [ - (13, 0x1C3, 0x333), - (8, 0x66, 0x5A), - (32, 0xDEADBEEF, 0xC0FFEE11) - ] - def testbench(): - for sck_idle, sck_edge in ((0, 0), (0, 1), (1, 0), (1, 1)): - for width, d_send, d_recv in tests: - yield from self._write_reg(dut, self.REG_CONFIG, ((width - 1) << 3) | (sck_edge << 1) | (sck_idle), 4) - yield from self._write_reg(dut, self.REG_DIV, 1, 1) - yield Tick() - yield from self._check_reg(dut, self.REG_STATUS, 0, 1) # not full - yield from self._write_reg(dut, self.REG_SEND_DATA, (d_send << (32 - width)), 4) - yield Tick() - for i in reversed(range(width)): - if sck_edge: - yield dut.spi_pins.cipo.i.eq((d_recv >> i) & 0x1) - else: - self.assertEqual((yield dut.spi_pins.copi.o), (d_send >> i) & 0x1) - self.assertEqual((yield dut.spi_pins.sck.o), 0 ^ sck_idle) - yield Tick() - self.assertEqual((yield dut.spi_pins.sck.o), 0 ^ sck_idle) - if sck_edge: - self.assertEqual((yield dut.spi_pins.copi.o), (d_send >> i) & 0x1) - else: - yield dut.spi_pins.cipo.i.eq((d_recv >> i) & 0x1) - yield Tick() - self.assertEqual((yield dut.spi_pins.sck.o), 1 ^ sck_idle) - yield Tick() - self.assertEqual((yield dut.spi_pins.sck.o), 1 ^ sck_idle) - yield Tick() - yield Tick() - yield Tick() - yield from self._check_reg(dut, self.REG_STATUS, 1, 1) # full - yield from self._check_reg(dut, self.REG_RECEIVE_DATA, d_recv, 4) # received correct data - yield from self._check_reg(dut, self.REG_STATUS, 0, 1) # not full - - sim = Simulator(dut) - sim.add_clock(1e-5) - sim.add_testbench(testbench) - with sim.write_vcd("spi_xfer_test.vcd", "spi_xfer_test.gtkw"): - sim.run() - - def test_divider(self): - dut = SPIPeripheral() - - def testbench(): - width = 8 - d_send = 0x73 - divide = 13 - - yield from self._write_reg(dut, self.REG_CONFIG, ((width - 1) << 3), 4) - yield from self._write_reg(dut, self.REG_DIV, divide, 1) - yield Tick() - yield from self._check_reg(dut, self.REG_STATUS, 0, 1) # not full - yield from self._write_reg(dut, self.REG_SEND_DATA, (d_send << (32 - width)), 4) - yield Tick() - for i in reversed(range(width)): - self.assertEqual((yield dut.spi_pins.copi.o),(d_send >> i) & 0x1) - self.assertEqual((yield dut.spi_pins.sck.o), 0) - for j in range(divide+1): yield Tick() - self.assertEqual((yield dut.spi_pins.sck.o), 1) - for j in range(divide+1): yield Tick() - yield Tick() - yield Tick() - yield from self._check_reg(dut, self.REG_STATUS, 1, 1) # full - sim = Simulator(dut) - sim.add_clock(1e-5) - sim.add_testbench(testbench) - with sim.write_vcd("spi_div_test.vcd", "spi_div_test.gtkw"): - sim.run() - -if __name__ == "__main__": - unittest.main() - diff --git a/my_design/software/drivers/i2c.h b/my_design/software/drivers/i2c.h deleted file mode 100644 index 2ea886c..0000000 --- a/my_design/software/drivers/i2c.h +++ /dev/null @@ -1,12 +0,0 @@ -#include -#ifndef I2C_H -#define I2C_H -typedef struct { - uint32_t divider; - uint32_t action; - uint32_t send_data; - uint32_t receive_data; - uint32_t status; -} i2c_regs_t; - -#endif diff --git a/my_design/software/drivers/spi.h b/my_design/software/drivers/spi.h deleted file mode 100644 index d9ed224..0000000 --- a/my_design/software/drivers/spi.h +++ /dev/null @@ -1,12 +0,0 @@ -#include -#ifndef SPI_H -#define SPI_H -typedef struct { - uint32_t config; - uint32_t divider; - uint32_t send_data; - uint32_t receive_data; - uint32_t status; -} spi_regs_t; - -#endif diff --git a/my_design/software/main.c b/my_design/software/main.c index 103c0e9..fade57e 100644 --- a/my_design/software/main.c +++ b/my_design/software/main.c @@ -7,41 +7,6 @@ char uart_getch_block(volatile uart_regs_t *uart) { return uart->rx.data; } -uint32_t spi_xfer(volatile spi_regs_t *spi, uint32_t data, uint32_t width) { - spi->config = ((width - 1) << 3) | 0x06; // CS=1, SCK_EDGE=1, SCK_IDLE=0 - spi->send_data = data << (32U - width); - while (!(spi->status & 0x1)) // wait for rx full - ; - spi->config = ((width - 1) << 3) | 0x02; // CS=0, SCK_EDGE=1, SCK_IDLE=0 - return spi->receive_data; -} - -void i2c_start(volatile i2c_regs_t *i2c) { - i2c->action = (1<<1); - while (i2c->status & 0x1) - ; -} - -int i2c_write(volatile i2c_regs_t *i2c, uint8_t data) { - i2c->send_data = data; - while (i2c->status & 0x1) - ; - return (i2c->status & 0x2) != 0; // check ACK -} - -uint8_t i2c_read(volatile i2c_regs_t *i2c) { - i2c->action = (1<<3); - while (i2c->status & 0x1) - ; - return i2c->receive_data; -} - -void i2c_stop(volatile i2c_regs_t *i2c) { - i2c->action = (1<<2); - while (i2c->status & 0x1) - ; -} - void main() { uart_init(UART_0, 25000000/115200); uart_init(UART_1, 25000000/115200); @@ -101,16 +66,16 @@ void main() { puts("SPI: "); - USER_SPI_0->divider = 2; + spi_init(USER_SPI_0, 2); // test 8 bit transfer - puthex(spi_xfer(USER_SPI_0, 0x5A, 8)); + puthex(spi_xfer(USER_SPI_0, 0x5A, 8, true)); puts(" "); // test an odd 21 bit transfer - puthex(spi_xfer(USER_SPI_0, 0x123456, 21)); + puthex(spi_xfer(USER_SPI_0, 0x123456, 21, true)); puts("\n"); - I2C_0->divider = 2; + i2c_init(I2C_0, 2); i2c_start(I2C_0); diff --git a/my_design/tests/events_reference.json b/my_design/tests/events_reference.json index 218f374..59c9437 100644 --- a/my_design/tests/events_reference.json +++ b/my_design/tests/events_reference.json @@ -89,55 +89,55 @@ { "timestamp": 868594, "peripheral": "uart_0", "event": "tx", "payload": 73 }, { "timestamp": 878172, "peripheral": "uart_0", "event": "tx", "payload": 58 }, { "timestamp": 887750, "peripheral": "uart_0", "event": "tx", "payload": 32 }, -{ "timestamp": 891390, "peripheral": "spi_0", "event": "select", "payload": "" }, -{ "timestamp": 893154, "peripheral": "spi_0", "event": "data", "payload": 90 }, -{ "timestamp": 895614, "peripheral": "spi_0", "event": "deselect", "payload": "" }, -{ "timestamp": 910788, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 921590, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 932392, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 943194, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 953996, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 964798, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 975600, "peripheral": "uart_0", "event": "tx", "payload": 54 }, -{ "timestamp": 986402, "peripheral": "uart_0", "event": "tx", "payload": 51 }, -{ "timestamp": 1000652, "peripheral": "uart_0", "event": "tx", "payload": 32 }, -{ "timestamp": 1003908, "peripheral": "spi_0", "event": "select", "payload": "" }, -{ "timestamp": 1005828, "peripheral": "spi_0", "event": "data", "payload": 1193046 }, -{ "timestamp": 1008132, "peripheral": "spi_0", "event": "deselect", "payload": "" }, -{ "timestamp": 1023306, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1034108, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1044910, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1056746, "peripheral": "uart_0", "event": "tx", "payload": 66 }, -{ "timestamp": 1068316, "peripheral": "uart_0", "event": "tx", "payload": 67 }, -{ "timestamp": 1079886, "peripheral": "uart_0", "event": "tx", "payload": 68 }, -{ "timestamp": 1091456, "peripheral": "uart_0", "event": "tx", "payload": 69 }, -{ "timestamp": 1103026, "peripheral": "uart_0", "event": "tx", "payload": 70 }, -{ "timestamp": 1119068, "peripheral": "uart_0", "event": "tx", "payload": 13 }, -{ "timestamp": 1124356, "peripheral": "uart_0", "event": "tx", "payload": 10 }, -{ "timestamp": 1127762, "peripheral": "i2c_0", "event": "start", "payload": "" }, -{ "timestamp": 1143772, "peripheral": "uart_0", "event": "tx", "payload": 73 }, -{ "timestamp": 1153350, "peripheral": "uart_0", "event": "tx", "payload": 50 }, -{ "timestamp": 1162928, "peripheral": "uart_0", "event": "tx", "payload": 67 }, -{ "timestamp": 1172506, "peripheral": "uart_0", "event": "tx", "payload": 58 }, -{ "timestamp": 1182084, "peripheral": "uart_0", "event": "tx", "payload": 32 }, -{ "timestamp": 1183922, "peripheral": "i2c_0", "event": "address", "payload": 160 }, -{ "timestamp": 1193452, "peripheral": "i2c_0", "event": "write", "payload": 51 }, -{ "timestamp": 1194624, "peripheral": "uart_0", "event": "tx", "payload": 97 }, -{ "timestamp": 1203770, "peripheral": "uart_0", "event": "tx", "payload": 97 }, -{ "timestamp": 1203920, "peripheral": "i2c_0", "event": "start", "payload": "" }, -{ "timestamp": 1207746, "peripheral": "i2c_0", "event": "address", "payload": 161 }, -{ "timestamp": 1218064, "peripheral": "uart_0", "event": "tx", "payload": 97 }, -{ "timestamp": 1228930, "peripheral": "uart_0", "event": "tx", "payload": 32 }, -{ "timestamp": 1248384, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1259186, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1269988, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1280790, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1291592, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1302394, "peripheral": "uart_0", "event": "tx", "payload": 48 }, -{ "timestamp": 1313196, "peripheral": "uart_0", "event": "tx", "payload": 50 }, -{ "timestamp": 1325032, "peripheral": "uart_0", "event": "tx", "payload": 68 }, -{ "timestamp": 1341074, "peripheral": "uart_0", "event": "tx", "payload": 13 }, -{ "timestamp": 1346362, "peripheral": "uart_0", "event": "tx", "payload": 10 }, -{ "timestamp": 1349496, "peripheral": "i2c_0", "event": "stop", "payload": "" } +{ "timestamp": 894866, "peripheral": "spi_0", "event": "select", "payload": "" }, +{ "timestamp": 896630, "peripheral": "spi_0", "event": "data", "payload": 90 }, +{ "timestamp": 900370, "peripheral": "spi_0", "event": "deselect", "payload": "" }, +{ "timestamp": 915672, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 926474, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 937276, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 948078, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 958880, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 969682, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 980484, "peripheral": "uart_0", "event": "tx", "payload": 54 }, +{ "timestamp": 991286, "peripheral": "uart_0", "event": "tx", "payload": 51 }, +{ "timestamp": 1005536, "peripheral": "uart_0", "event": "tx", "payload": 32 }, +{ "timestamp": 1009048, "peripheral": "spi_0", "event": "select", "payload": "" }, +{ "timestamp": 1010968, "peripheral": "spi_0", "event": "data", "payload": 1193046 }, +{ "timestamp": 1014552, "peripheral": "spi_0", "event": "deselect", "payload": "" }, +{ "timestamp": 1029854, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1040656, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1051458, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1063294, "peripheral": "uart_0", "event": "tx", "payload": 66 }, +{ "timestamp": 1074864, "peripheral": "uart_0", "event": "tx", "payload": 67 }, +{ "timestamp": 1086434, "peripheral": "uart_0", "event": "tx", "payload": 68 }, +{ "timestamp": 1098004, "peripheral": "uart_0", "event": "tx", "payload": 69 }, +{ "timestamp": 1109574, "peripheral": "uart_0", "event": "tx", "payload": 70 }, +{ "timestamp": 1125616, "peripheral": "uart_0", "event": "tx", "payload": 13 }, +{ "timestamp": 1130904, "peripheral": "uart_0", "event": "tx", "payload": 10 }, +{ "timestamp": 1137002, "peripheral": "i2c_0", "event": "start", "payload": "" }, +{ "timestamp": 1153012, "peripheral": "uart_0", "event": "tx", "payload": 73 }, +{ "timestamp": 1162590, "peripheral": "uart_0", "event": "tx", "payload": 50 }, +{ "timestamp": 1172168, "peripheral": "uart_0", "event": "tx", "payload": 67 }, +{ "timestamp": 1181746, "peripheral": "uart_0", "event": "tx", "payload": 58 }, +{ "timestamp": 1191324, "peripheral": "uart_0", "event": "tx", "payload": 32 }, +{ "timestamp": 1193162, "peripheral": "i2c_0", "event": "address", "payload": 160 }, +{ "timestamp": 1202692, "peripheral": "i2c_0", "event": "write", "payload": 51 }, +{ "timestamp": 1203864, "peripheral": "uart_0", "event": "tx", "payload": 97 }, +{ "timestamp": 1213010, "peripheral": "uart_0", "event": "tx", "payload": 97 }, +{ "timestamp": 1213160, "peripheral": "i2c_0", "event": "start", "payload": "" }, +{ "timestamp": 1216986, "peripheral": "i2c_0", "event": "address", "payload": 161 }, +{ "timestamp": 1227304, "peripheral": "uart_0", "event": "tx", "payload": 97 }, +{ "timestamp": 1238170, "peripheral": "uart_0", "event": "tx", "payload": 32 }, +{ "timestamp": 1257624, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1268426, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1279228, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1290030, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1300832, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1311634, "peripheral": "uart_0", "event": "tx", "payload": 48 }, +{ "timestamp": 1322436, "peripheral": "uart_0", "event": "tx", "payload": 50 }, +{ "timestamp": 1334272, "peripheral": "uart_0", "event": "tx", "payload": 68 }, +{ "timestamp": 1350314, "peripheral": "uart_0", "event": "tx", "payload": 13 }, +{ "timestamp": 1355602, "peripheral": "uart_0", "event": "tx", "payload": 10 }, +{ "timestamp": 1358736, "peripheral": "i2c_0", "event": "stop", "payload": "" } ] }