add simple wishbone GPIO peripheral
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 5 Sep 2020 13:27:30 +0000 (14:27 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 5 Sep 2020 13:27:30 +0000 (14:27 +0100)
src/soc/bus/simple_gpio.py [new file with mode: 0644]
src/soc/bus/test/wb_rw.py [new file with mode: 0644]
src/soc/fu/shift_rot/test/test_pipe_caller.py

diff --git a/src/soc/bus/simple_gpio.py b/src/soc/bus/simple_gpio.py
new file mode 100644 (file)
index 0000000..c42c47f
--- /dev/null
@@ -0,0 +1,187 @@
+"""Simple GPIO peripheral on wishbone
+
+This is an extremely simple GPIO peripheral intended for use in XICS
+testing, however it could also be used as an actual GPIO peripheral
+"""
+
+from nmigen import Elaboratable, Module, Signal, Record, Array
+from nmigen.utils import log2_int
+from nmigen.cli import rtlil
+from soc.minerva.wishbone import make_wb_layout
+from nmutil.util import wrap
+
+cxxsim = False
+if cxxsim:
+    from nmigen.sim.cxxsim import Simulator, Settle
+else:
+    from nmigen.back.pysim import Simulator, Settle
+
+
+class SimpleGPIO(Elaboratable):
+
+    def __init__(self, n_gpio=16):
+        self.n_gpio = n_gpio
+        class Spec: pass
+        spec = Spec()
+        spec.addr_wid = 30
+        spec.mask_wid = 4
+        spec.reg_wid = 32
+        self.bus = Record(make_wb_layout(spec), name="icp_wb")
+        self.gpio_o = Signal(n_gpio)
+
+    def elaborate(self, platform):
+        m = Module()
+        comb, sync = m.d.comb, m.d.sync
+
+        bus = self.bus
+        wb_rd_data = bus.dat_r
+        wb_wr_data = bus.dat_w
+        wb_ack = bus.ack
+        gpio_o = self.gpio_o
+
+        comb += wb_ack.eq(0)
+
+        gpio_addr = Signal(log2_int(self.n_gpio))
+        gpio_a = Array(list(gpio_o))
+
+        with m.If(bus.cyc & bus.stb):
+            comb += wb_ack.eq(1) # always ack
+            comb += gpio_addr.eq(bus.adr)
+            with m.If(bus.we): # write
+                sync += gpio_a[gpio_addr].eq(wb_wr_data[0])
+            with m.Else(): # read
+                comb += wb_rd_data.eq(gpio_a[gpio_addr])
+
+        return m
+
+    def __iter__(self):
+        for field in self.bus.fields.values():
+            yield field
+        yield self.gpio_o
+
+    def ports(self):
+        return list(self)
+
+
+def wb_write(dut, addr, data, sel=True):
+
+    # write wb
+    yield dut.bus.we.eq(1)
+    yield dut.bus.cyc.eq(1)
+    yield dut.bus.stb.eq(1)
+    yield dut.bus.sel.eq(0b1111 if sel else 0b1) # 32-bit / 8-bit
+    yield dut.bus.adr.eq(addr)
+    yield dut.bus.dat_w.eq(data)
+
+    # wait for ack to go high
+    while True:
+        ack = yield dut.bus.ack
+        print ("ack", ack)
+        if ack:
+            break
+        yield # loop until ack
+        yield dut.bus.stb.eq(0) # drop stb so only 1 thing into pipeline
+
+    # leave cyc/stb valid for 1 cycle while writing
+    yield
+
+    # clear out before returning data
+    yield dut.bus.cyc.eq(0)
+    yield dut.bus.stb.eq(0)
+    yield dut.bus.we.eq(0)
+    yield dut.bus.adr.eq(0)
+    yield dut.bus.sel.eq(0)
+    yield dut.bus.dat_w.eq(0)
+
+
+def wb_read(dut, addr, sel=True):
+
+    # read wb
+    yield dut.bus.cyc.eq(1)
+    yield dut.bus.stb.eq(1)
+    yield dut.bus.we.eq(0)
+    yield dut.bus.sel.eq(0b1111 if sel else 0b1) # 32-bit / 8-bit
+    yield dut.bus.adr.eq(addr)
+
+    # wait for ack to go high
+    while True:
+        ack = yield dut.bus.ack
+        print ("ack", ack)
+        if ack:
+            break
+        yield # loop until ack
+        yield dut.bus.stb.eq(0) # drop stb so only 1 thing into pipeline
+
+    # get data on same cycle that ack raises
+    data = yield dut.bus.dat_r
+
+    # leave cyc/stb valid for 1 cycle while reading
+    yield
+
+    # clear out before returning data
+    yield dut.bus.cyc.eq(0)
+    yield dut.bus.stb.eq(0)
+    yield dut.bus.we.eq(0)
+    yield dut.bus.adr.eq(0)
+    yield dut.bus.sel.eq(0)
+    return data
+
+
+def read_gpio(gpio, addr):
+    data = yield from wb_read(gpio, addr)
+    print ("gpio%d" % addr, hex(data), bin(data))
+    return data
+
+
+def sim_gpio(gpio):
+
+    # GPIO0
+    data = yield from read_gpio(gpio, 0) # read gpio addr  0
+    assert data == 0
+    
+    yield from wb_write(gpio, 0, 1) # write gpio addr 0
+
+    data = yield from read_gpio(gpio, 0) # read gpio addr  0
+    assert data == 1
+
+    # GPIO1
+    data = yield from read_gpio(gpio, 1) # read gpio addr  1
+    assert data == 0
+    
+    yield from wb_write(gpio, 1, 1) # write gpio addr 1
+
+    data = yield from read_gpio(gpio, 1) # read gpio addr  1
+    assert data == 1
+
+    # GPIO0
+    data = yield from read_gpio(gpio, 0) # read gpio addr  0
+    assert data == 1
+    
+    yield from wb_write(gpio, 0, 0) # write gpio addr 0
+
+    data = yield from read_gpio(gpio, 0) # read gpio addr  0
+    assert data == 0
+
+
+def test_gpio():
+
+    dut = SimpleGPIO()
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_gpio.il", "w") as f:
+        f.write(vl)
+
+    m = Module()
+    m.submodules.xics_icp = dut
+
+    sim = Simulator(m)
+    sim.add_clock(1e-6)
+
+    sim.add_sync_process(wrap(sim_gpio(dut)))
+    sim_writer = sim.write_vcd('test_gpio.vcd')
+    with sim_writer:
+        sim.run()
+
+
+if __name__ == '__main__':
+    test_gpio()
+
diff --git a/src/soc/bus/test/wb_rw.py b/src/soc/bus/test/wb_rw.py
new file mode 100644 (file)
index 0000000..8ee79b0
--- /dev/null
@@ -0,0 +1,67 @@
+"""Wishbone read/write utility routines
+"""
+
+
+def wb_write(bus, addr, data, sel=True):
+
+    # write wb
+    yield bus.we.eq(1)
+    yield bus.cyc.eq(1)
+    yield bus.stb.eq(1)
+    yield bus.sel.eq(0b1111 if sel else 0b1) # 32-bit / 8-bit
+    yield bus.adr.eq(addr)
+    yield bus.dat_w.eq(data)
+
+    # wait for ack to go high
+    while True:
+        ack = yield bus.ack
+        print ("ack", ack)
+        if ack:
+            break
+        yield # loop until ack
+        yield bus.stb.eq(0) # drop stb so only 1 thing into pipeline
+
+    # leave cyc/stb valid for 1 cycle while writing
+    yield
+
+    # clear out before returning data
+    yield bus.cyc.eq(0)
+    yield bus.stb.eq(0)
+    yield bus.we.eq(0)
+    yield bus.adr.eq(0)
+    yield bus.sel.eq(0)
+    yield bus.dat_w.eq(0)
+
+
+def wb_read(bus, addr, sel=True):
+
+    # read wb
+    yield bus.cyc.eq(1)
+    yield bus.stb.eq(1)
+    yield bus.we.eq(0)
+    yield bus.sel.eq(0b1111 if sel else 0b1) # 32-bit / 8-bit
+    yield bus.adr.eq(addr)
+
+    # wait for ack to go high
+    while True:
+        ack = yield bus.ack
+        print ("ack", ack)
+        if ack:
+            break
+        yield # loop until ack
+        yield bus.stb.eq(0) # drop stb so only 1 thing into pipeline
+
+    # get data on same cycle that ack raises
+    data = yield bus.dat_r
+
+    # leave cyc/stb valid for 1 cycle while reading
+    yield
+
+    # clear out before returning data
+    yield bus.cyc.eq(0)
+    yield bus.stb.eq(0)
+    yield bus.we.eq(0)
+    yield bus.adr.eq(0)
+    yield bus.sel.eq(0)
+    return data
+
index d1760bcd93df22637eeb7eb05c6928aa6d99aead..34d4736e7773129a7ff5e902c732e33cd8ad9a0d 100644 (file)
@@ -79,7 +79,7 @@ def set_alu_inputs(alu, dec2, sim):
 
 class ShiftRotTestCase(TestAccumulatorBase):
 
-    def case_0_proof_regression_rlwnm(self):
+    def cse_0_proof_regression_rlwnm(self):
         lst = ["rlwnm 3, 1, 2, 16, 20"]
         initial_regs = [0] * 32
         initial_regs[1] = 0x7ffdbffb91b906b9
@@ -87,7 +87,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
         print(initial_regs[1], initial_regs[2])
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_regression_rldicr_0(self):
+    def cse_regression_rldicr_0(self):
         lst = ["rldicr. 29, 19, 1, 21"]
         initial_regs = [0] * 32
         initial_regs[1] = 0x3f
@@ -98,7 +98,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
         self.add_case(Program(lst, bigendian), initial_regs,
                                 initial_sprs=initial_sprs)
 
-    def case_regression_rldicr_1(self):
+    def cse_regression_rldicr_1(self):
         lst = ["rldicr. 29, 19, 1, 21"]
         initial_regs = [0] * 32
         initial_regs[1] = 0x3f
@@ -106,7 +106,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
 
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_shift(self):
+    def cse_shift(self):
         insns = ["slw", "sld", "srw", "srd", "sraw", "srad"]
         for i in range(20):
             choice = random.choice(insns)
@@ -117,7 +117,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
             print(initial_regs[1], initial_regs[2])
             self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_shift_arith(self):
+    def cse_shift_arith(self):
         lst = ["sraw 3, 1, 2"]
         initial_regs = [0] * 32
         initial_regs[1] = random.randint(0, (1 << 64)-1)
@@ -125,7 +125,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
         print(initial_regs[1], initial_regs[2])
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_sld_rb_too_big(self):
+    def cse_sld_rb_too_big(self):
         lst = ["sld 3, 1, 4",
                ]
         initial_regs = [0] * 32
@@ -141,7 +141,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
         initial_regs[4] = 0 # no shift; output should equal input
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_shift_once(self):
+    def cse_shift_once(self):
         lst = ["slw 3, 1, 4",
                "slw 3, 1, 2"]
         initial_regs = [0] * 32
@@ -150,7 +150,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
         initial_regs[4] = 0x00
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_rlwinm(self):
+    def cse_rlwinm(self):
         for i in range(10):
             mb = random.randint(0, 31)
             me = random.randint(0, 31)
@@ -162,45 +162,45 @@ class ShiftRotTestCase(TestAccumulatorBase):
             initial_regs[1] = random.randint(0, (1 << 64)-1)
             self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_rlwimi(self):
+    def cse_rlwimi(self):
         lst = ["rlwimi 3, 1, 5, 20, 6"]
         initial_regs = [0] * 32
         initial_regs[1] = 0xdeadbeef
         initial_regs[3] = 0x12345678
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_rlwnm(self):
+    def cse_rlwnm(self):
         lst = ["rlwnm 3, 1, 2, 20, 6"]
         initial_regs = [0] * 32
         initial_regs[1] = random.randint(0, (1 << 64)-1)
         initial_regs[2] = random.randint(0, 63)
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_rldicl(self):
+    def cse_rldicl(self):
         lst = ["rldicl 3, 1, 5, 20"]
         initial_regs = [0] * 32
         initial_regs[1] = random.randint(0, (1 << 64)-1)
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_rldicr(self):
+    def cse_rldicr(self):
         lst = ["rldicr 3, 1, 5, 20"]
         initial_regs = [0] * 32
         initial_regs[1] = random.randint(0, (1 << 64)-1)
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_regression_extswsli(self):
+    def cse_regression_extswsli(self):
         lst = [f"extswsli 3, 1, 34"]
         initial_regs = [0] * 32
         initial_regs[1] = 0x5678
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_regression_extswsli_2(self):
+    def cse_regression_extswsli_2(self):
         lst = [f"extswsli 3, 1, 7"]
         initial_regs = [0] * 32
         initial_regs[1] = 0x3ffffd7377f19fdd
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_regression_extswsli_3(self):
+    def cse_regression_extswsli_3(self):
         lst = [f"extswsli 3, 1, 0"]
         initial_regs = [0] * 32
         #initial_regs[1] = 0x80000000fb4013e2
@@ -210,7 +210,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
         #initial_regs[1] = 0x3ffffd73f7f19fdd
         self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_extswsli(self):
+    def cse_extswsli(self):
         for i in range(40):
             sh = random.randint(0, 63)
             lst = [f"extswsli 3, 1, {sh}"]
@@ -218,7 +218,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
             initial_regs[1] = random.randint(0, (1 << 64)-1)
             self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_rlc(self):
+    def cse_rlc(self):
         insns = ["rldic", "rldicl", "rldicr"]
         for i in range(20):
             choice = random.choice(insns)
@@ -229,7 +229,7 @@ class ShiftRotTestCase(TestAccumulatorBase):
             initial_regs[1] = random.randint(0, (1 << 64)-1)
             self.add_case(Program(lst, bigendian), initial_regs)
 
-    def case_ilang(self):
+    def cse_ilang(self):
         pspec = ShiftRotPipeSpec(id_wid=2)
         alu = ShiftRotBasePipe(pspec)
         vl = rtlil.convert(alu, ports=alu.ports())