From 6dfd19e394790558ed41a2991624bbc331d1150d Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sat, 5 Sep 2020 14:27:30 +0100 Subject: [PATCH] add simple wishbone GPIO peripheral --- src/soc/bus/simple_gpio.py | 187 ++++++++++++++++++ src/soc/bus/test/wb_rw.py | 67 +++++++ src/soc/fu/shift_rot/test/test_pipe_caller.py | 36 ++-- 3 files changed, 272 insertions(+), 18 deletions(-) create mode 100644 src/soc/bus/simple_gpio.py create mode 100644 src/soc/bus/test/wb_rw.py diff --git a/src/soc/bus/simple_gpio.py b/src/soc/bus/simple_gpio.py new file mode 100644 index 00000000..c42c47f3 --- /dev/null +++ b/src/soc/bus/simple_gpio.py @@ -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 index 00000000..8ee79b0c --- /dev/null +++ b/src/soc/bus/test/wb_rw.py @@ -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 + diff --git a/src/soc/fu/shift_rot/test/test_pipe_caller.py b/src/soc/fu/shift_rot/test/test_pipe_caller.py index d1760bcd..34d4736e 100644 --- a/src/soc/fu/shift_rot/test/test_pipe_caller.py +++ b/src/soc/fu/shift_rot/test/test_pipe_caller.py @@ -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()) -- 2.30.2