import sys from typing import Callable from amaranth import * from amaranth import Elaboratable from amaranth.back import verilog, cxxrtl def cmd(hdl, tb:Callable): ''' Very simple command line interface Accepts a Elaboratable class and a testbench function The elaboratable class must have a ports attribute that is a dict of in and out ports {'in': [Signals()], 'out': [Signals()]} The testbench function should only expect one argument: path to save .vcd file ''' if len(sys.argv) <= 1: exit() if sys.argv[1] == "sim": tb(sys.argv[0].replace('.py', '.vcd')) exit() if sys.argv[1] == "v": out = verilog.convert(hdl, ports=hdl.ports['in'] + hdl.ports['out']) with open(sys.argv[0].replace('.py', '.v'), 'w') as f: f.write(out) elif sys.argv[1] == "cc": out = cxxrtl.convert(hdl, ports=hdl.ports['in'] + hdl.ports['out']) with open(sys.argv[0].replace('.py', '.cc'), 'w') as f: f.write(out) class DubbleBuff(Elaboratable): ''' This module wraps another modules input and output with a buffer This is usefull for doing timeing analysis on combinational logic An instance of a module should be passed, not the module itself ''' def __init__(self, sub_module: Elaboratable): assert sub_module.ports is not None, 'sub_module must have ports' self.sub_module = sub_module ports_in = [Signal(port.width, name=port.name + '_inbuf') for port in sub_module.ports['in']] ports_out = [Signal(port.width, name=port.name + '_outbuf') for port in sub_module.ports['out']] self.ports = {'in': ports_in, 'out': ports_out} def elaborate(self, platform): m = Module() m.submodules.sub = self.sub_module for i in range(len(self.ports['in'])): m.d.sync += self.sub_module.ports['in'][i].eq(self.ports['in'][i]) for i in range(len(self.ports['out'])): m.d.sync += self.ports['out'][i].eq(self.sub_module.ports['out'][i]) return m