def test_clock_interaction(): mods = [] num_child = 4 for i in range(num_child): mod = Generator("mod", True) in_ = mod.input("in", 4) out_ = mod.output("out", 4) clk = mod.clock("clk") seq = mod.sequential((posedge, clk)) seq.add_stmt(out_.assign(in_)) mods.append(mod) parent = Generator("parent", True) clk = parent.clock("clk") in_ = parent.input("in", 4) out = parent.output("out", 4) for i, mod in enumerate(mods): parent.add_child("mod{0}".format(i), mod) parent.wire(mod.ports.clk, clk) if i == 0: continue parent.wire(mod.ports["in"], mods[i - 1].ports.out) parent.wire(mods[0].ports["in"], in_) parent.wire(out, mods[-1].ports.out) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(parent, filename=filename, insert_debug_info=True, debug_db_filename=debug_db)
def test_inst_id(): def create_mod(): m = Generator("mod", True) in_ = m.input("in", 1) out = m.output("out", 1) comb = m.combinational() comb.add_stmt(out.assign(in_)) return m mod = Generator("parent", True) input_ = mod.input("in", 1) output = mod.output("out", 1) mods = [create_mod() for _ in range(2)] expr = None for i, m_ in enumerate(mods): mod.add_child("mod{0}".format(i), m_) mod.wire(input_, m_.ports["in"]) if expr is None: expr = m_.ports["out"] else: expr = expr & m_.ports["out"] mod.wire(output, expr) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") verilog(mod, insert_debug_info=True, debug_db_filename=debug_db, optimize_passthrough=False) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * FROM instance") res = c.fetchall() assert len(res) == 3 conn.close()
def test_assert(): from kratos import assert_ mod = Generator("mod", True) in_ = mod.input("in", 1) out_ = mod.output("out", 1) @always_comb def code(): # we introduce this bug on purpose out_ = in_ - 1 assert_(out_ == in_) mod.add_always(code) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(mod, filename=filename, insert_debug_info=True, debug_db_filename=debug_db) with open(filename) as f: content = f.read() assert "assert (out == in) else" in content conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * FROM breakpoint") lines = c.fetchall() assert len(lines) == 2 # they are only one line apart assert abs(lines[0][2] - lines[1][2]) == 1 conn.close() # once we remove the assertion, it should not be there _kratos.passes.remove_assertion(mod.internal_generator) src = verilog(mod)[0]["mod"] assert "assert" not in src
def test_nested_scope(): from kratos import clog2 mod = Generator("FindHighestBit", True) width = 4 data = mod.input("data", width) h_bit = mod.output("h_bit", clog2(width)) done = mod.var("done", 1) @always_comb def find_bit(): done = 0 h_bit = 0 for i in range(width): if ~done: if data[i]: done = 1 h_bit = i mod.add_always(find_bit, label="block") verilog(mod, insert_debug_info=True) block = mod.get_marked_stmt("block") last_if = block[-1] for i in range(len(last_if.then_[-1].then_)): stmt = last_if.then_[-1].then_[i] context = stmt.scope_context if len(context) > 0: assert "i" in context is_var, var = context["i"] assert not is_var assert var == "3"
def test_wire(): mod = Generator("mod", True) in_ = mod.input("in", 1) out_ = mod.output("out", 1) # context a = 1 mod.wire(out_, in_) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(mod, filename=filename, insert_debug_info=True, debug_db_filename=debug_db) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * FROM breakpoint") assert len(c.fetchall()) == 1 c.execute( """SELECT variable.value FROM variable, context WHERE context.name = ? AND context.variable_id = variable.id""", "a") v = int(c.fetchone()[0]) assert v == a conn.close()
def test_data_if(): class Mod(Generator): def __init__(self, bool_flag): super().__init__("mod1") self.in_ = self.port("in", 1, PortDirection.In) self.out_ = self.port("out", 1, PortDirection.Out) self.bool_flag = bool_flag self.add_code(self.code) def code(self): if self.bool_flag: if self.in_ == self.const(1, 1): self.out_ = 1 else: self.out_ = 0 else: if self.in_ == self.const(0, 1): self.out_ = 0 else: self.out_ = 1 mod = Mod(True) mod_src = verilog(mod) src = mod_src["mod1"] assert is_valid_verilog(src) assert "in == 1'h1" in src # need to clear the context, otherwise it will be a different module name Generator.clear_context() mod = Mod(False) mod_src = verilog(mod) src = mod_src["mod1"] assert is_valid_verilog(src) assert "in == 1'h0" in src
def test_array_packed(): from kratos import PackedStruct mod = Generator("mod", True) aa = mod.var("a", 2, size=(2, 4), packed=True) struct = PackedStruct("s", [("read", 16, False), ("data", 16, False)]) ss = mod.var_packed("s", struct) mod.add_stmt(aa.assign(4)) setattr(mod, "sss", ss) mod.add_stmt(ss["read"].assign(0)) mod.add_stmt(ss["data"].assign(1)) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") verilog(mod, debug_db_filename=debug_db, insert_debug_info=True) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute( "SELECT variable.value, generator_variable.name FROM variable, generator_variable WHERE variable.id = generator_variable.variable_id" ) vars_ = c.fetchall() c.execute( "SELECT variable.value, context.name FROM variable, context WHERE variable.id = context.variable_id" ) vars_ += c.fetchall() correct_struct, correct_array, correct_self = False, False, False for value, name in vars_: if value == "a[1][3]" and name == "aa.1.3": correct_array = True if value == "s.read" and name == "ss.read": correct_struct = True if "self.sss" in name: correct_self = True assert correct_array and correct_struct, correct_self conn.close()
def test_design_hierarchy(): from functools import reduce mods = [] num_child = 4 num_child_child = 3 def add_child(m): output = None outputs_ = [] for c in range(num_child_child): child = Generator("child", True) m.add_child("child{0}".format(c), child) in__ = child.input("in", 4) out__ = child.output("out", 4) clk__ = child.clock("clk") m.wire(m.ports.clk, clk__) if output is None: m.wire(m.ports["in"], in__) else: m.wire(output, in__) output = out__ seq_ = child.sequential((posedge, clk__)) seq_.add_stmt(out__.assign(in__)) outputs_.append(out__) return outputs_ for i in range(num_child): mod = Generator("mod", True) mod.input("in", 4) out_ = mod.output("out", 4) clk = mod.clock("clk") seq = mod.sequential((posedge, clk)) outputs = add_child(mod) seq.add_stmt(out_.assign(reduce(lambda x, y: x + y, outputs))) mods.append(mod) parent = Generator("parent") clk = parent.clock("clk") in_ = parent.input("in", 4) out = parent.output("out", 4) for i, mod in enumerate(mods): parent.add_child("mod{0}".format(i), mod) parent.wire(mod.ports.clk, clk) if i == 0: continue parent.wire(mod.ports["in"], mods[i - 1].ports.out) parent.wire(mods[0].ports["in"], in_) parent.wire(out, mods[-1].ports.out) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(parent, filename=filename, insert_debug_info=True, debug_db_filename=debug_db) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * FROM hierarchy") mods = c.fetchall() assert len(mods) == num_child_child * num_child + num_child conn.close()
def test_context(): class Mod(Generator): def __init__(self, width): super().__init__("mod", True) in_ = self.input("in", width) out = self.output("out", width) sel = self.input("sel", width) # test self variables self.out = out self.width = width @always_comb def code(): if sel: out = 0 else: for i in range(width): out[i] = 1 self.add_always(code) mod = Mod(4) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(mod, filename=filename, insert_debug_info=True, debug_db_filename=debug_db) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * FROM context") variables = c.fetchall() assert len(variables) > 20 conn.close()
def test_verilog_file(): mod = PassThroughTop() with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "mod.sv") verilog(mod, filename=filename, debug=True, optimize_passthrough=False) with open(filename) as f: src = f.read() assert is_valid_verilog(src)
def test_port_interface(): mod = Generator("mod") mod.interface(ConfigInterface(), "port_interface", is_port=True) with tempfile.TemporaryDirectory() as temp: filename = os.path.join(temp, "mod.sv") verilog(mod, filename=filename) with open(filename) as f: content = f.read() assert "endinterface" in content
def test_db_dump(): mod = Generator("mod", True) comb = mod.combinational() comb.add_stmt(mod.var("a", 1).assign(mod.var("b", 1))) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") # hashing and generate verilog verilog(mod, insert_debug_info=True, debug_db_filename=debug_db) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * from breakpoint") result = c.fetchall() assert len(result) == 1 conn.close()
def test_more_debug2(): class Top(Generator): def __init__(self): super().__init__("top", True) self.port("in", 1, PortDirection.In) self.port("out", 1, PortDirection.Out) pass_through = PassThroughMod() self.add_child_generator("pass", pass_through) self.wire( self["pass"].ports["in"], self.ports["in"], ) self.add_code(self.code_block) def code_block(self): self.ports.out = self["pass"].ports.out mod = Top() mod_src, debug_info = verilog(mod, debug=True) src = mod_src["top"] debug = debug_info["top"] assert is_valid_verilog(src) assert len(debug) > 3
def test_create(): class Mod(Generator): def __init__(self, width, is_clone=False): super().__init__(f"mod_{width}", is_clone=is_clone) self.in_ = self.port("in", width, PortDirection.In) self.out_ = self.port("out", width, PortDirection.Out) self.wire(self.out_, self.in_) mod1 = Mod.create(width=1) mod2 = Mod.create(width=2) mod3 = Mod.create(width=1) assert not mod1.is_cloned assert not mod2.is_cloned assert mod3.is_cloned assert mod3.def_instance == mod1 # modify mod 3 mod3.initialize_clone() mod3.in_.width = 3 mod3.out_.width = 3 assert not mod3.is_cloned mod_src = verilog(mod3) # we didn't change the name assert "mod_1" in mod_src assert "2:0" in mod_src["mod_1"] assert is_valid_verilog(mod_src["mod_1"])
def test_pass(): def change_name(generator): class Visitor(ASTVisitor): def __init__(self): ASTVisitor.__init__(self) def visit(self, node): if isinstance(node, Port): # rename the output port if node.name == "out": node.name = "test" visitor = Visitor() visitor.visit_root(generator) class Mod1(Generator): def __init__(self): super().__init__("mod1", True) self.in_ = self.port("in", 1, PortDirection.In) self.out_ = self.port("out", 1, PortDirection.Out) self.wire(self.out_, self.in_) mod = Mod1() mod_src = verilog(mod, additional_passes={"name_change": change_name}) src = mod_src["mod1"] assert is_valid_verilog(src) assert "logic test" in src
def test_static_eval_for_loop(): class Mod(Generator): def __init__(self, num_loop): super().__init__("mod1", True) self.in_ = self.port("in", 1, PortDirection.In) self.out_ = self.port("out", num_loop, PortDirection.Out) self.num_loop = num_loop self.add_code(self.code) def code(self): if self.in_ == self.const(1, 1): for i in range(self.num_loop): self.out_[i] = 1 else: for i in range(self.num_loop): self.out_[i] = 0 loop = 4 mod = Mod(loop) mod_src, mod_debug = verilog(mod, debug=True) src = mod_src["mod1"] mod_mapping = mod_debug["mod1"] lines = list(mod_mapping.keys()) lines.sort() for ii in range(len(mod_mapping) - loop, len(mod_mapping) - 1): assert mod_mapping[lines[-1]][-1] == mod_mapping[lines[ii]][-1] assert is_valid_verilog(src)
def test_debug(): class Mod(Generator): def __init__(self): super().__init__("mod1", True) self.in_ = self.port("in", 1, PortDirection.In) self.out_1 = self.port("out1", 1, PortDirection.Out) self.out_2 = self.port("out2", 1, PortDirection.Out) self.wire(self.out_1, self.in_) self.add_code(self.code) def code(self): self.out_2 = self.in_ mod = Mod() mod_src, mod_debug = verilog(mod, debug=True) src_mapping = mod_debug["mod1"] assert len(src_mapping) == 7 verilog_lines = mod_src["mod1"].split("\n") verilog_ln = 0 for ln, line in enumerate(verilog_lines): if "assign out1 = in;" in line: verilog_ln = ln + 1 break fn, ln = src_mapping[verilog_ln][0] with open(fn) as f: python_lns = f.readlines() assert "self.wire(self.out_1, self.in_)" in python_lns[ln - 1]
def test_fanout_mod_inst(): class Mod2(Generator): def __init__(self): super().__init__("mod2") self.in_ = self.port("in", 1, PortDirection.In) self.out_ = self.port("out", 1, PortDirection.Out) self.mod_1 = PassThroughMod() self.mod_2 = PassThroughMod() self.add_child_generator("mod1", self.mod_1) self.add_child_generator("mod2", self.mod_2) self.wire(self.in_, self.mod_1.in_) self.wire(self.in_, self.mod_2.in_) self.add_code(self.code) def code(self): self.out_ = self.mod_1.out_ + self.mod_2.out_ mod = Mod2() mod_src = verilog(mod, optimize_passthrough=False) assert "mod2" in mod_src src = mod_src["mod2"] assert is_valid_verilog(src)
def test_clone(): class Mod2(Generator): def __init__(self): super().__init__("mod2") self.in_ = self.port("in", 2, PortDirection.In) self.out_ = self.port("out", 2, PortDirection.Out) self.child1 = PassThroughMod.clone() self.child2 = PassThroughMod.clone() self.add_child_generator("child1", self.child1) self.add_child_generator("child2", self.child2) self.add_code(self.code) def code(self): self.child1.ports["in"] = self.in_[0] self.child2.ports["in"] = self.in_[1] self.out_[0] = self.child1.ports.out self.out_[1] = self.child2.ports.out mod = Mod2() assert not mod.child1.is_cloned assert mod.child2.is_cloned mod_src = verilog(mod, False, False, False) src = mod_src["mod2"] assert is_valid_verilog(src)
def test_more_debug1(): mod = PassThroughTop() mod_src, debug_info = verilog(mod, debug=True) src = mod_src["top"] debug = debug_info["top"] assert is_valid_verilog(src) assert len(debug) > 3
def test_one_hot_decoder(): cases = 16 num_tests = 10 output_size = 20 decoder = OneHotDecoder(cases, output_size) mod_src = verilog(decoder) with tempfile.TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, decoder.name + ".sv") with open(filename, "w+") as f: for value in mod_src.values(): f.write(value) f.write("\n") # import it as magma circuit circuit = magma.DefineFromVerilogFile(filename, target_modules=[decoder.name], shallow=True)[0] tester = fault.Tester(circuit) tester.zero_inputs() for i in range(num_tests): value = random.randrange(cases) tester.poke(circuit.S, value) tester.eval() tester.expect(circuit.O, 1 << value) tester.compile_and_run(target="verilator", skip_compile=True, directory=tempdir, flags=["-Wno-fatal"])
def test_regression_2(): from kratos.func import dpi_function @dpi_function(8) def func(arg0): pass class Mod(Generator): def __init__(self): super().__init__("mod", debug=True) self._in = self.input("in", 2) self._out = self.output("out", 8) self._reg = self.var("val", 8) self.add_always(self.code) @always_comb def code(self): # because the types are inferred # implicit const conversion doesn't work here self._reg = func(const(1, 2)) self._out = func(const(1, 2)) mod = Mod() mod_src = verilog(mod) assert "func" in mod_src[1]
def build(self, src_nodes, view): gen_spec = importlib.util.spec_from_file_location( self.gen_name, self.src_path) gen_mod = importlib.util.module_from_spec(gen_spec) gen_spec.loader.exec_module(gen_mod) vlog_gen = getattr(gen_mod, self.gen_name) if len(src_nodes) > 1: config = {} for src_node in src_nodes: config[src_node.name] = src_node.config_dict else: config = src_nodes[0].config_dict kratos.verilog(vlog_gen(**config), filename=self.snk_path) return self.snk_path
def test_async_reg(): reg_width = 16 reg = AsyncReg(reg_width) # produce verilog verilog_src = verilog(reg) assert "register" in verilog_src reg_src = verilog_src["register"] assert is_valid_verilog(reg_src)
def test_seq_debug(): class Mod(Generator): def __init__(self): super().__init__("mod", True) # ports self.in_ = self.input("in1", 1) self.clock("clk") for i in range(4): self.output("out{0}".format(i), 1) self.add_always(self.code1) self.add_always(self.code2) @always_comb def code1(self): if self.in_ == 0: self.ports.out0 = 0 self.ports.out1 = 0 else: self.ports.out0 = 1 self.ports.out1 = 1 @always_ff((posedge, "clk")) def code2(self): if self.in_ == 0: self.ports.out2 = 0 self.ports.out3 = 0 else: self.ports.out2 = 1 self.ports.out3 = 1 mod = Mod() with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(mod, filename=filename, insert_debug_info=True, debug_db_filename=debug_db) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT * FROM breakpoint WHERE id=7") result = c.fetchall() assert len(result) == 1 conn.close()
def test_md_array(): mod = Generator("mod") a = mod.var("a", 16, size=(4, 2), packed=True) b = mod.var("b", 16, size=(4, 2), packed=True) c = mod.var("c", 16, size=(2,), packed=True) mod.wire(b, a) mod.wire(c, a[1]) src = verilog(mod)["mod"] assert "logic [3:0][1:0][15:0] a;" in src
def test_pass_through(): mod = PassThroughTop() # turn off pass through module optimization since it will remove # mod2 completely mod_src = verilog(mod, optimize_passthrough=True) assert "top" in mod_src assert "mod1" not in mod_src assert is_valid_verilog(mod_src["top"]) assert "mod1" not in mod_src["top"]
def test_regression_flatten_array_param(): from _kratos import create_wrapper_flatten mod = Generator("mod") p = mod.parameter("param", value=16) mod.input("in", width=p) inst = create_wrapper_flatten(mod.internal_generator, "wrapper") gen = Generator("", internal_generator=inst) src = verilog(gen)["wrapper"] assert "parameter param = 32'h10" in src
def test_metadata(): mod = Generator("mod", True) mod.input("in", 1) mod.output("out", 1) mod.wire(mod.ports.out, mod.ports["in"]) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") filename = os.path.join(temp, "test.sv") verilog(mod, filename=filename, insert_debug_info=True, debug_db_filename=debug_db) conn = sqlite3.connect(debug_db) c = conn.cursor() c.execute("SELECT value FROM metadata WHERE name = ?", ["top_name"]) value = c.fetchone()[0] assert value == "mod" conn.close()
def test_ssa_debug(): mod = Generator("mod", debug=True) a = mod.var("a", 4) clk = mod.clock("clk") b = mod.var("b", 1) loop_size = 4 @always_comb def logic1(): a = 0 if b: for i in range(loop_size): a = a + i @always_ff((posedge, clk)) def logic2(): if a == 1: b = 1 else: b = 0 mod.add_always(logic1, ssa_transform=True) mod.add_always(logic2) with tempfile.TemporaryDirectory() as temp: debug_db = os.path.join(temp, "debug.db") verilog(mod, insert_debug_info=True, debug_db_filename=debug_db, ssa_transform=True) # assert the line number tracking conn = sqlite3.connect(debug_db) c = conn.cursor() idx = get_line(" a = a + i") c.execute("SELECT * FROM breakpoint WHERE line_num=?", (idx, )) result = c.fetchall() assert len(result) == loop_size assert "a_4" in result[0][-1] # check the context variable c.execute( "SELECT * FROM context_variable WHERE context_variable.name = 'i'") result = c.fetchall() assert len(result) == loop_size conn.close()