def make_print(self, i, action): port_names = [] prefix = self.get_verilator_prefix() for port in action.ports: if isinstance(port, fault.WrappedVerilogInternalPort): path = port.path.replace(".", "->") name = f"{prefix}->{path}" elif isinstance(port, PortWrapper): port = port.select_path name = port.verilator_path if len(port) > 2: name = f"{prefix}->" + name if self.verilator_version >= 3.856: if len(port) > 2: self.debug_includes.add(f"{port[0].circuit.name}") for item in port[1:-1]: circuit_name = type(item.instance).name self.debug_includes.add(f"{circuit_name}") else: name = verilator_name(port.name) port_names.append(name) ports = ", ".join(f"top->{name}" for name in port_names) if ports: ports = ", " + ports return [f'printf("{action.format_str}"{ports});']
def make_poke(self, i, action): if self.verilator_version > 3.874: prefix = f"{self.circuit_name}" else: prefix = f"v" if isinstance(action.port, fault.WrappedVerilogInternalPort): path = action.port.path.replace(".", "->") name = f"{prefix}->{path}" elif isinstance(action.port, SelectPath): name = "" if len(action.port) > 2: # TODO: Find the version that they changed this, 3.874 is known # to use top->v instead of top->{circuit_name} name += f"{prefix}->" name += action.port.verilator_path elif isinstance(action.port, actions.Var): name = action.port.name else: name = verilator_name(action.port.name) # Special case poking internal registers is_reg_poke = isinstance(action.port, SelectPath) and \ isinstance(action.port[-1], fault.WrappedVerilogInternalPort) \ and action.port[-1].path == "outReg" if isinstance(action.value, BitVector) and \ action.value.num_bits > max_bits: pokes = [] # For some reason, verilator chunks it by 32 instead of max_bits slice_range = 32 for i in range(math.ceil(action.value.num_bits / slice_range)): value = action.value[i * slice_range:min( (i + 1) * slice_range, action.value.num_bits)] pokes += [f"top->{name}[{i}] = {value};"] if is_reg_poke: raise NotImplementedError() return pokes else: value = action.value value = self.process_value(action.port, value) value = self.process_bitwise_assign(action.port, name, value) if isinstance(action.port, actions.Var): result = [f"{name} = {value};"] else: result = [f"top->{name} = {value};"] # Hack to support verilator's semantics, need to set the register # mux values for expected behavior if is_reg_poke: action.port[-1].path = "out" result += self.make_poke(i, action) action.port[-1].path = "in" result += self.make_poke(i, action) if "enable_mux" in action.port[-3].instance_map: mux_inst = action.port[-3].instance_map["enable_mux"] action.port[-2] = InstanceWrapper(mux_inst, action.port[-3]) action.port[-1] = type(mux_inst).I0 result += self.make_poke(i, action) return result
def make_expect(self, i, action): # For verilator, if an expect is "AnyValue" we don't need to # perform the expect. if value_utils.is_any(action.value): return [] prefix = self.get_verilator_prefix() if isinstance(action.port, fault.WrappedVerilogInternalPort): path = action.port.path.replace(".", "->") name = f"{prefix}->{path}" debug_name = name elif isinstance(action.port, SelectPath): name = action.port.verilator_path if len(action.port) > 2: name = f"{prefix}->" + name debug_name = action.port[-1].debug_name else: name = verilator_name(action.port.name) debug_name = action.port.debug_name value = action.value if isinstance(value, actions.Peek): value = self.process_peek(value) elif isinstance(value, PortWrapper): value = f"top->{prefix}->" + value.select_path.verilator_path if isinstance(action.value, BitVector) and \ action.value.num_bits > max_bits: asserts = [] # For some reason, verilator chunks it by 32 instead of max_bits slice_range = 32 for j in range(math.ceil(action.value.num_bits / slice_range)): value = action.value[j * slice_range:min( (j + 1) * slice_range, action.value.num_bits)] asserts += [ f"my_assert(top->{name}[{j}], {value}, " f"{i}, \"{debug_name}\");" ] return asserts else: value = self.process_value(action.port, value) port_value = f"top->{name}" port_value = self.process_bitwise_expect(action.port, port_value) port = action.port if isinstance(port, SelectPath): port = port[-1] elif isinstance(port, fault.WrappedVerilogInternalPort): port = port.type_ if isinstance(port, m.Digital): port_len = 1 else: port_len = len(port) mask = (1 << port_len) - 1 return [ f"my_assert({port_value}, {value} & {mask}, " f"{i}, \"{debug_name}\");" ]
def make_step(self, i, action): name = verilator_name(action.clock.name) code = [] code.append("top->eval();") for step in range(action.steps): code.append("#if VM_TRACE") code.append("tracer->dump(main_time);") code.append("#endif") code.append(f"top->{name} ^= 1;") code.append("top->eval();") code.append("main_time += 5;") return code
def _make_print_args(self, ports): port_names = [] prefix = self.get_verilator_prefix() for port in ports: if isinstance(port, fault.WrappedVerilogInternalPort): path = port.path.replace(".", "->") name = f"{prefix}->{path}" elif isinstance(port, PortWrapper): port = port.select_path name = port.verilator_path if len(port) > 2: name = f"{prefix}->" + name else: name = verilator_name(port.name) port_names.append(name) return tuple(f"top->{name}" for name in port_names)
def make_print(self, i, action): port_names = [] prefix = self.get_verilator_prefix() for port in action.ports: if isinstance(port, fault.WrappedVerilogInternalPort): path = port.path.replace(".", "->") name = f"{prefix}->{path}" elif isinstance(port, PortWrapper): port = port.select_path name = port.verilator_path if len(port) > 2: name = f"{prefix}->" + name else: name = verilator_name(port.name) port_names.append(name) ports = ", ".join(f"top->{name}" for name in port_names) if ports: ports = ", " + ports return [f'printf("{action.format_str}"{ports});']
def __init__(self, *args, clock=None, **kwargs): if clock is None: raise ValueError("Clock required") self.clock = verilator_name(clock.name)
def make_expect(self, i, action): # For verilator, if an expect is "AnyValue" we don't need to # perform the expect. if value_utils.is_any(action.value): return [] prefix = self.get_verilator_prefix() if isinstance(action.port, fault.WrappedVerilogInternalPort): path = action.port.path.replace(".", "->") name = f"{prefix}->{path}" debug_name = name elif isinstance(action.port, SelectPath): name = action.port.verilator_path if len(action.port) > 2: name = f"{prefix}->" + name debug_name = action.port[-1].debug_name else: name = verilator_name(action.port.name) debug_name = action.port.debug_name value = action.value if isinstance(value, actions.Peek): value = self.process_peek(value) elif isinstance(value, PortWrapper): value = f"top->{prefix}->" + value.select_path.verilator_path user_msg = () if action.msg is not None: if isinstance(action.msg, str): user_msg += (action.msg, ) else: assert isinstance(action.msg, tuple) user_msg += (action.msg[0], ) user_msg += self._make_print_args(action.msg[1:]) if isinstance(action.value, BitVector) and \ action.value.num_bits > max_bits: asserts = [] # For some reason, verilator chunks it by 32 instead of max_bits slice_range = 32 for j in range(math.ceil(action.value.num_bits / slice_range)): value = action.value[j * slice_range:min( (j + 1) * slice_range, action.value.num_bits)] asserts.append( self._make_assert(f"((unsigned int) top->{name}[{j}])", f"((unsigned int) {value})", i, f"\"{debug_name}\"", user_msg)) return asserts else: value = self.process_value(action.port, value) port_value = f"top->{name}" port_value = self.process_bitwise_expect(action.port, port_value) port = action.port if isinstance(port, SelectPath): port = port[-1] elif isinstance(port, fault.WrappedVerilogInternalPort): port = port.type_ if isinstance(port, m.Digital): port_len = 1 else: port_len = len(port) # deal with real-valued expressions if isinstance(port, RealType): got = f"({port_value})" expected = f"({value})" style = 'scientific' else: mask = (1 << port_len) - 1 got = f"((unsigned int) {port_value})" expected = f"(unsigned int) ({value} & {mask})" style = 'hex' return [ self._make_assert(got=got, expected=expected, i=i, port=f'"{debug_name}"', user_msg=user_msg, below=action.below, above=action.above, style=style) ]
def make_poke(self, i, action): if self.verilator_version > 3.874: prefix = f"{self.circuit_name}" else: prefix = f"v" if isinstance(action.port, fault.WrappedVerilogInternalPort): path = action.port.path.replace(".", "->") name = f"{prefix}->{path}" elif isinstance(action.port, SelectPath): name = "" if len(action.port) > 2: # TODO: Find the version that they changed this, 3.874 is known # to use top->v instead of top->{circuit_name} name += f"{prefix}->" name += action.port.verilator_path if len(action.port) > 2: self.debug_includes.add(f"{action.port[0].circuit.name}") for item in action.port[1:-1]: circuit = type(item.instance) circuit_name = circuit.verilog_name # Verilator specializes each parametrization into a separate # mdoule, this is an attempt to reverse engineer the naming # scheme if circuit_name == "coreir_reg": circuit_name += "_" circuit_name += f"_C1" # Posedge clock circuit_name += f"_I{circuit.coreir_configargs['init']}" circuit_name += f"_W{circuit.coreir_genargs['width']}" elif circuit_name == "coreir_reg_arst": circuit_name += "_" circuit_name += f"_I{circuit.coreir_configargs['init']}" if circuit.coreir_genargs['width'] != 1: circuit_name += f"_W{circuit.coreir_genargs['width']}" self.debug_includes.add(f"{circuit_name}") else: name = verilator_name(action.port.name) # Special case poking internal registers is_reg_poke = isinstance(action.port, SelectPath) and \ isinstance(action.port[-1], fault.WrappedVerilogInternalPort) \ and action.port[-1].path == "outReg" if isinstance(action.value, BitVector) and \ action.value.num_bits > max_bits: pokes = [] # For some reason, verilator chunks it by 32 instead of max_bits slice_range = 32 for i in range(math.ceil(action.value.num_bits / slice_range)): value = action.value[i * slice_range:min( (i + 1) * slice_range, action.value.num_bits)] pokes += [f"top->{name}[{i}] = {value};"] if is_reg_poke: raise NotImplementedError() return pokes else: value = action.value if isinstance(value, actions.FileRead): mask = "FF" * value.file.chunk_size value = " | ".join(f"{value.file.name_without_ext}_in[{i}]" for i in range(value.file.chunk_size)) value = f"({value}) & 0x{mask}" value = self.process_value(action.port, value) value = self.process_bitwise_assign(action.port, name, value) result = [f"top->{name} = {value};"] # Hack to support verilator's semantics, need to set the register # mux values for expected behavior if is_reg_poke: action.port[-1].path = "out" result += self.make_poke(i, action) action.port[-1].path = "in" result += self.make_poke(i, action) if "enable_mux" in action.port[-3].instance_map: mux_inst = action.port[-3].instance_map["enable_mux"] action.port[-2] = InstanceWrapper(mux_inst, action.port[-3]) action.port[-1] = type(mux_inst).I0 result += self.make_poke(i, action) return result