Restructured Amaranth code to be able to import sibling modules

This commit is contained in:
Dennis Brentjes 2022-01-22 19:28:29 +01:00
parent 7780e14887
commit 4a2d2b4881
91 changed files with 518 additions and 356 deletions

1
re-bba/.gitignore vendored
View File

@ -1,3 +1,4 @@
*.vcd
*.v
**/__pycache__
venv

View File

@ -1,74 +0,0 @@
from amaranth import *
class UpCounter(Elaboratable):
"""
A 16-bit up counter with a fixed limit.
Parameters
----------
limit : int
The value at which the counter overflows.
Attributes
----------
en : Signal, in
The counter is incremented if ``en`` is asserted, and retains
its value otherwise.
ovf : Signal, out
``ovf`` is asserted when the counter reaches its limit.
"""
def __init__(self, limit):
self.limit = limit
# Ports
self.en = Signal()
self.ovf = Signal()
# State
self.count = Signal(16)
def elaborate(self, platform):
m = Module()
m.d.comb += self.ovf.eq(self.count == self.limit)
with m.If(self.en):
with m.If(self.ovf):
m.d.sync += self.count.eq(0)
with m.Else():
m.d.sync += self.count.eq(self.count + 1)
return m
from amaranth.sim import Simulator
dut = UpCounter(25)
def bench():
# Disabled counter should not overflow.
yield dut.en.eq(0)
for _ in range(30):
yield
assert not (yield dut.ovf)
# Once enabled, the counter should overflow in 25 cycles.
yield dut.en.eq(1)
for _ in range(25):
yield
assert not (yield dut.ovf)
yield
assert (yield dut.ovf)
# The overflow should clear in one cycle.
yield
assert not (yield dut.ovf)
sim = Simulator(dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(bench)
with sim.write_vcd("up_counter.vcd"):
sim.run()
from amaranth_boards.icebreaker import *
ICEBreakerPlatform().build(UpCounter(25), do_program=True)

View File

@ -1,110 +0,0 @@
import enum
import sys
import os
from amaranth.build import Platform
from amaranth import *
from amaranth.sim import Simulator
class ClockState(enum.Enum):
LOW = 0
FALLING = 1
HIGH = 2
RISING = 3
class ExiClock(Elaboratable):
def __init__(self):
# Ports
self.exiClk = Signal()
self.exiClkState = Signal(2)
# State
self.prevExiClkValid = Signal()
self.prevExiClk = Signal()
self.prevExiClkState = Signal(2)
def elaborate(self, platform: Platform):
m = Module()
with m.If(self.prevExiClkValid):
with m.If(self.prevExiClkState == ClockState.FALLING):
m.d.comb += self.exiClkState.eq(ClockState.LOW)
with m.Elif(self.prevExiClkState == ClockState.RISING):
m.d.comb += self.exiClkState.eq(ClockState.HIGH)
with m.Else():
with m.If(self.prevExiClk ^ self.exiClk):
m.d.comb += self.exiClkState.eq(Cat(1, self.exiClk))
with m.Else():
m.d.comb += self.exiClkState.eq(Cat(0, self.exiClk))
m.d.sync += self.prevExiClkState.eq(self.exiClkState)
m.d.sync += self.prevExiClk.eq(self.exiClk)
m.d.sync += self.prevExiClkValid.eq(1)
return m
#####
# TestBench
#####
class TestBench:
def __init__(self):
pass
def FlipExiClock(self, dut):
yield dut.exiClk.eq(~dut.exiClk)
def clockTest(self):
dut = self.dut
yield dut.exiClk.eq(0)
yield
yield from self.FlipExiClock(dut)
yield
assert (yield dut.exiClkState) == ClockState.RISING.value
yield
assert (yield dut.exiClkState) == ClockState.HIGH.value
yield
yield from self.FlipExiClock(dut)
yield
assert (yield dut.exiClkState) == ClockState.FALLING.value
yield
assert (yield dut.exiClkState) == ClockState.LOW.value
yield
def simulate(self):
self.dut = ExiClock()
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.clockTest)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ExiClock.vcd"):
sim.run()
#####
# Main portion
#####
def main():
if(len(sys.argv) == 2):
if sys.argv[1] == "s":
bench = TestBench()
bench.simulate()
if sys.argv[1] == "v":
mod = ExiClock()
with open(os.path.dirname(os.path.abspath(__file__)) + "/ExiClock.v", "w") as f:
f.write(verilog.convert(mod, ports=[mod.exiClk, mod.exiClkState]))
else:
bench = TestBench()
bench.simulate()
if __name__ == "__main__":
main()

View File

@ -1,22 +0,0 @@
from amaranth import *
class LEDBlinker(Elaboratable):
def elaborate(self, platform):
m = Module()
led = platform.request("led")
half_freq = int(platform.default_clk_frequency // 2)
timer = Signal(range(half_freq + 1))
with m.If(timer == half_freq):
m.d.sync += led.eq(~led)
m.d.sync += timer.eq(0)
with m.Else():
m.d.sync += timer.eq(timer + 1)
return m
from amaranth_boards.icebreaker import *
ICEBreakerPlatform().build(LEDBlinker(), do_program=True)

View File

@ -0,0 +1,39 @@
from amaranth.build import Platform
from amaranth import *
class Debouncer(Elaboratable):
def __init__(self, cycles: int):
#signals
self.input = Signal()
self.output = Signal()
#state
self.count = Signal(range(0, cycles+1), reset=cycles)
self.state = Signal()
self.prevInValid = Signal()
self.prevIn = Signal()
def elaborate(self, platform: Platform):
m = Module()
with m.If(self.prevInValid):
with m.If(self.count == 0):
with m.If(self.prevIn ^ self.input):
m.d.sync += self.count.eq(self.count.reset)
m.d.sync += self.state.eq(self.input)
with m.Elif(self.input == self.state):
m.d.sync += self.count.eq(self.count.reset)
m.d.sync += self.state.eq(self.input)
with m.Else():
m.d.sync += self.count.eq(self.count - 1)
with m.Else():
m.d.sync += self.count.eq(0)
m.d.sync += self.prevInValid.eq(Const(1))
m.d.sync += self.state.eq(self.input)
m.d.sync += self.prevIn.eq(self.input)
m.d.comb += self.output.eq(self.state)
return m

View File

@ -0,0 +1,45 @@
import enum
from amaranth.build import Platform
from amaranth import *
class ClockState(enum.Enum):
LOW = 0
FALLING = 1
HIGH = 2
RISING = 3
class ExiClock(Elaboratable):
def __init__(self):
# Ports
self.exiClk = Signal()
self.exiClkState = Signal(2)
# State
self.prevExiClkValid = Signal()
self.prevExiClk = Signal()
self.prevExiClkState = Signal(2)
def elaborate(self, platform: Platform):
m = Module()
with m.If(self.prevExiClkValid):
with m.If(self.prevExiClkState == ClockState.FALLING):
m.d.comb += self.exiClkState.eq(ClockState.LOW)
with m.Elif(self.prevExiClkState == ClockState.RISING):
m.d.comb += self.exiClkState.eq(ClockState.HIGH)
with m.Else():
with m.If(self.prevExiClk ^ self.exiClk):
m.d.comb += self.exiClkState.eq(Cat(1, self.exiClk))
with m.Else():
m.d.comb += self.exiClkState.eq(Cat(0, self.exiClk))
m.d.sync += self.prevExiClkState.eq(self.exiClkState)
m.d.sync += self.prevExiClk.eq(self.exiClk)
m.d.sync += self.prevExiClkValid.eq(1)
return m

View File

@ -0,0 +1,23 @@
from amaranth.build import Platform
from amaranth import *
from ExiDecoders.GetId import GetId
class ExiDecoder(Elaboratable):
def __init__(self):
#ports
self.request = Signal(16)
self.request_type = Signal(1)
#state
def elaborate(self, platform: Platform):
m = Module()
getId = GetId()
m.submodules += getId
m.d.comb += getId.request.eq(self.request)
m.d.comb += self.request_type[0].eq(getId.isGetId)
return m

View File

@ -0,0 +1,51 @@
from ReBba.Components.ShiftRegister import ShiftRegister
from ReBba.Components.ExiClock import ClockState
from amaranth import Const, Elaboratable, Module, Signal
from amaranth.build import Platform
class ExiRequest(Elaboratable):
def __init__(self):
#ports
self.request = Signal(16)
self.nen = Signal(1)
self.rst = Signal(1)
self.exiClkState = Signal(2)
self.exiIn = Signal(1)
self.requestComplete = Signal(1)
#state
self.disableShift = Signal(1, reset=0)
self.shiftRegister = ShiftRegister(16)
self.clockCount = Signal(5, reset=0)
def elaborate(self, platform: Platform):
m = Module()
m.submodules += self.shiftRegister
m.d.comb += self.request.eq(self.shiftRegister.data)
m.d.comb += self.shiftRegister.inb.eq(self.exiIn)
m.d.comb += self.shiftRegister.exiClkState.eq(self.exiClkState)
m.d.comb += self.shiftRegister.nen.eq(self.disableShift)
with m.If(~self.nen):
with m.If(self.clockCount != 16):
with m.If(self.exiClkState == ClockState.FALLING):
m.d.sync += self.clockCount.eq(self.clockCount + 1)
with m.If(self.clockCount == 16):
m.d.comb += self.disableShift.eq(Const(1))
m.d.comb += self.requestComplete.eq(Const(1))
with m.If(self.rst):
m.d.comb += self.request.eq(self.request.reset)
m.d.sync += self.clockCount.eq(self.clockCount.reset)
m.d.comb += self.disableShift.eq(self.disableShift.reset)
m.d.comb += self.shiftRegister.rst.eq(self.rst)
return m

View File

@ -0,0 +1,29 @@
from ReBba.Components.ExiClock import ClockState
from amaranth import *
from amaranth.build import Platform
class ShiftRegister(Elaboratable):
def __init__(self, width):
self.width = width
# Ports
self.nen = Signal()
self.exiClkState = Signal(2)
self.rst = Signal()
self.inb = Signal()
self.data = Signal(self.width, reset=0)
def elaborate(self, platform: Platform):
m = Module()
with m.If(self.rst):
m.d.sync += self.data.eq(self.data.reset)
with m.If(~self.nen):
with m.If(self.exiClkState == ClockState.FALLING):
m.d.sync += self.data.eq(self.data.shift_left(1) | self.inb )
return m

View File

@ -0,0 +1,19 @@
from amaranth.build import Platform
from amaranth import Signal, Const, Module, Elaboratable
class GetId(Elaboratable):
def __init__(self):
self.request = Signal(16)
self.isGetId = Signal()
pass
def elaborate(self, platform: Platform):
m = Module()
with m.If(self.request == 0):
m.d.comb += self.isGetId.eq(Const(1))
with m.Else():
m.d.comb += self.isGetId.eq(Const(0))
return m

View File

@ -7,7 +7,9 @@ from amaranth.sim import Simulator
from amaranth_boards.icebreaker import *
from ExiClock import ExiClock
from ShiftRegister import ShiftRegister
from ExiRequest import ExiRequest
from Debouncer import Debouncer
from ExiDecoder import ExiDecoder
class ReBba(Elaboratable):
def __init__(self):
@ -16,6 +18,9 @@ class ReBba(Elaboratable):
def elaborate(self, platform: Platform):
m = Module()
uledg = platform.request("led_g", 0)
uledr = platform.request("led_r", 0)
led1 = platform.request("led_g", 1)
led2 = platform.request("led_g", 4)
led3 = platform.request("led_g", 2)
@ -26,23 +31,36 @@ class ReBba(Elaboratable):
btn2 = platform.request("button", 2)
btn3 = platform.request("button", 3)
debounce1 = Debouncer(10000)
m.submodules += debounce1
m.d.comb += debounce1.input.eq(btn3)
exiClk = ExiClock()
m.submodules += exiClk
sr = ShiftRegister(5)
m.submodules += sr
m.d.comb += led1.eq(sr.data[0])
m.d.comb += led2.eq(sr.data[1])
m.d.comb += led3.eq(sr.data[2])
m.d.comb += led4.eq(sr.data[3])
m.d.comb += led5.eq(sr.data[4])
m.d.comb += exiClk.exiClk.eq(debounce1.output)
m.d.comb += exiClk.exiClk.eq(btn3)
exiReq = ExiRequest()
m.submodules += exiReq
m.d.comb += sr.rst.eq(btn2)
m.d.comb += sr.inb.eq(btn1)
m.d.comb += sr.nen.eq(Const(0))
m.d.comb += sr.exiClkState.eq(exiClk.exiClkState)
m.d.comb += exiReq.exiClkState.eq(exiClk.exiClkState)
m.d.comb += exiReq.exiIn.eq(btn1)
m.d.comb += exiReq.rst.eq(btn2)
m.d.comb += led1.eq(exiReq.request[0])
m.d.comb += led2.eq(exiReq.request[1])
m.d.comb += led3.eq(exiReq.request[2])
m.d.comb += led4.eq(exiReq.request[3])
m.d.comb += led5.eq(exiReq.requestComplete)
exiDec = ExiDecoder()
m.submodules += exiDec
m.d.comb += exiDec.request.eq(exiReq.request)
m.d.comb += uledr.eq(exiDec.request_type[0])
m.d.comb += uledg.eq(exiReq.requestComplete)
return m

View File

@ -0,0 +1,54 @@
from ReBba.Components.Debouncer import Debouncer
from amaranth import Const
from amaranth.sim import Simulator
import os
class TestBench:
def __init__(self):
pass
def holdTest(self):
dut = self.dut
yield
yield
yield dut.input.eq(Const(1))
yield
yield dut.input.eq(Const(0))
yield
for _ in range(5):
assert(yield(dut.output) == 1)
yield
yield dut.input.eq(Const(1))
yield
yield dut.input.eq(Const(0))
yield
for _ in range(16):
assert(yield(dut.output) == 1)
yield
assert(yield(dut.output) == 0)
yield
yield
def simulate(self):
self.dut = Debouncer(15)
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.holdTest)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/Debouncer.vcd"):
sim.run()
def main():
bench = TestBench()
bench.simulate()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,48 @@
from ReBba.Components.ExiClock import ClockState, ExiClock
from ReBba.TestBenches.SimHelpers.ExiSimHelper import exiClockCycle
from amaranth.sim import Simulator
import os
class TestBench:
def __init__(self):
pass
def FlipExiClock(self, dut):
yield dut.exiClk.eq(~dut.exiClk)
def clockTest(self):
dut = self.dut
yield dut.exiClk.eq(0)
yield
yield from self.FlipExiClock(dut)
yield
assert (yield dut.exiClkState) == ClockState.RISING.value
yield
assert (yield dut.exiClkState) == ClockState.HIGH.value
yield
yield from self.FlipExiClock(dut)
yield
assert (yield dut.exiClkState) == ClockState.FALLING.value
yield
assert (yield dut.exiClkState) == ClockState.LOW.value
yield
def simulate(self):
self.dut = ExiClock()
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.clockTest)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ExiClock.vcd"):
sim.run()
def main():
bench = TestBench()
bench.simulate()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,65 @@
from ReBba.Components.ExiRequest import ExiRequest
from SimHelpers.ExiSimHelper import exiClockCycle, resetDut
from amaranth import Const
from amaranth.sim import Simulator
import os
class TestBench:
def __init__(self):
pass
def requestTest(self):
dut = self.dut
yield dut.nen.eq(Const(0))
def afterLow0():
assert((yield dut.requestComplete) == 0)
def afterLow1():
assert((yield dut.requestComplete) == 1)
for i in range(15):
yield dut.exiIn.eq(~dut.exiIn)
yield from exiClockCycle(dut.exiClkState, AfterLow=afterLow0)
yield dut.exiIn.eq(~dut.exiIn)
yield from exiClockCycle(dut.exiClkState, AfterLow=afterLow1)
assert((yield dut.requestComplete) == 1)
yield from exiClockCycle(dut.exiClkState)
assert((yield dut.requestComplete) == 1)
yield from resetDut(dut.rst)
for i in range(15):
yield dut.exiIn.eq(~dut.exiIn)
yield from exiClockCycle(dut.exiClkState, AfterLow=afterLow0)
yield dut.exiIn.eq(~dut.exiIn)
yield from exiClockCycle(dut.exiClkState, AfterLow=afterLow1)
def simulate(self):
self.dut = ExiRequest()
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.requestTest)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ExiRequest.vcd"):
sim.run()
#####
# Main portion
#####
def main():
bench = TestBench()
bench.simulate()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,79 @@
from amaranth import Elaboratable
from ReBba.Components.ShiftRegister import ShiftRegister
from ReBba.TestBenches.SimHelpers.ExiSimHelper import exiClockCycle
from amaranth.sim import Simulator
import os
class TestBench:
def __init__(self):
pass
def enabled_test(self):
dut = self.dut
def afterAny():
assert not (yield dut.data)
# Disabled ShiftRegister should not Change.
yield dut.nen.eq(1)
for _ in range(10):
yield dut.inb.eq(~dut.inb)
yield from exiClockCycle(dut.exiClkState, AfterHigh=afterAny, AfterFalling=afterAny, AfterLow=afterAny, AfterRising=afterAny)
def normal_operation(self):
dut = self.dut
def oracle(i):
if i == 0: return 0x01
if i == 1: return 0x02
if i == 2: return 0x05
if i == 3: return 0x0A
if i == 4: return 0x15
if i == 5: return 0x2A
if i == 6: return 0x55
if i == 7: return 0xAA
if i == 8: return 0x55
if i == 9: return 0xAA
expectedOutput = 0
def afterLow():
assert (yield dut.data) == expectedOutput
yield dut.nen.eq(0)
for i in range(10):
expectedOutput = oracle(i)
yield dut.inb.eq(~dut.inb)
yield from exiClockCycle(dut.exiClkState, AfterLow=afterLow)
def simulate(self):
self.dut = ShiftRegister(8)
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.enabled_test)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ShiftRegister_enable.vcd"):
sim.run()
self.dut = ShiftRegister(8)
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.normal_operation)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ShiftRegister_shift.vcd"):
sim.run()
def main():
bench = TestBench()
bench.simulate()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,30 @@
from typing import Callable
from ReBba.Components.ExiClock import ClockState
from amaranth import Const, Elaboratable, Signal
def empty():
yield None
def exiClockCycle(exiClock: Signal, AfterHigh: Callable = empty, AfterFalling: Callable = empty, AfterLow: Callable = empty, AfterRising: Callable = empty):
yield exiClock.eq(ClockState.HIGH)
yield
yield from AfterHigh()
yield
yield exiClock.eq(ClockState.FALLING)
yield from AfterFalling()
yield exiClock.eq(ClockState.LOW)
yield
yield from AfterLow()
yield
yield exiClock.eq(ClockState.RISING)
yield from AfterRising()
def resetDut(rst: Signal):
yield
yield rst.eq(Const(1))
yield
yield rst.eq(Const(0))
yield

View File

@ -1,136 +0,0 @@
import os
import sys
from amaranth import *
from amaranth.build import Platform
from amaranth.back import verilog
from amaranth.sim import Simulator
from ExiClock import ClockState
#####
# Hardware Description.
#####
class ShiftRegister(Elaboratable):
def __init__(self, width):
self.width = width
# Ports
self.nen = Signal()
self.exiClkState = Signal(2)
self.rst = Signal()
self.inb = Signal()
self.data = Signal(self.width, reset=0)
def elaborate(self, platform: Platform):
m = Module()
with m.If(self.rst):
m.d.sync += self.data.eq(self.data.reset)
with m.If(~self.nen):
with m.If(self.exiClkState == ClockState.FALLING):
m.d.sync += self.data.eq(self.data.shift_left(1) | self.inb )
return m
#####
# TestBench
#####
class TestBench:
def __init__(self):
pass
def simExiClock(self, dut):
yield dut.exiClk.eq(~dut.exiClk)
def enabled_test(self):
dut = self.dut
# Disabled ShiftRegister should not Change.
yield dut.nen.eq(1)
for _ in range(10):
yield dut.inb.eq(~dut.inb)
yield from self.simExiClock(dut)
yield from self.simExiClock(dut)
assert not (yield dut.data)
yield
yield
yield
yield from self.simExiClock(dut)
yield from self.simExiClock(dut)
assert not (yield dut.data)
yield
yield
yield
def normal_operation(self):
dut = self.dut
def oracle(i):
if i == 0: return 0x01
if i == 1: return 0x02
if i == 2: return 0x05
if i == 3: return 0x0A
if i == 4: return 0x15
if i == 5: return 0x2A
if i == 6: return 0x55
if i == 7: return 0xAA
if i == 8: return 0x55
if i == 9: return 0xAA
yield dut.nen.eq(0)
for i in range(10):
yield dut.inb.eq(~dut.inb)
yield from self.simExiClock(dut)
yield
yield
yield from self.simExiClock(dut)
yield
yield
assert (yield dut.data) == oracle(i)
def simulate(self):
self.dut = ShiftRegister(8)
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.enabled_test)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ShiftRegister_enable.vcd"):
sim.run()
self.dut = ShiftRegister(8)
sim = Simulator(self.dut)
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(self.normal_operation)
with sim.write_vcd(os.path.dirname(os.path.abspath(__file__)) + "/ShiftRegister_shift.vcd"):
sim.run()
#####
# Main portion
#####
def main():
if(len(sys.argv) == 2):
if sys.argv[1] == "s":
bench = TestBench()
bench.simulate()
if sys.argv[1] == "v":
mod = ShiftRegister(8)
with open(os.path.dirname(os.path.abspath(__file__)) + "/shift_register.v", "w") as f:
f.write(verilog.convert(mod, ports=[mod.exiClk, mod.inb, mod.data]))
else:
bench = TestBench()
bench.simulate()
if __name__ == "__main__":
main()

3
re-bba/setup.py Normal file
View File

@ -0,0 +1,3 @@
from setuptools import setup, find_packages
setup(name='ReBba', version='0.1', packages=find_packages())