Skip to content

Commit 52a2794

Browse files
committed
Adding automatic reset for registers
1 parent 86832fe commit 52a2794

File tree

7 files changed

+517
-304
lines changed

7 files changed

+517
-304
lines changed

pyrtl/compilesim.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,21 @@ def __init__(
8686
self._remove_untraceable()
8787

8888
self.default_value = default_value
89-
self._regmap, self._memmap = register_value_map, memory_value_map
89+
self._regmap = {} # Updated below
90+
self._memmap = memory_value_map
9091
self._uid_counter = 0
9192
self.varname = {} # mapping from wires and memories to C variables
9293

94+
for r in self.block.wirevector_subset(Register):
95+
rval = register_value_map.get(r, r.reset_value)
96+
if rval is None:
97+
rval = self.default_value
98+
self._regmap[r] = rval
99+
93100
# Passing the dictionary objects themselves since they aren't updated anywhere.
94101
# If that's ever not the case, will need to pass in deep copies of them like done
95102
# for the normal Simulation so we retain the initial values that had.
96-
self.tracer._set_initial_values(default_value, register_value_map, memory_value_map)
103+
self.tracer._set_initial_values(default_value, self._regmap, self._memmap)
97104

98105
self._create_dll()
99106
self._initialize_mems()
@@ -432,9 +439,11 @@ def _declare_wv(self, write, w):
432439
write('const uint64_t {name}[{limbs}] = {val};'.format(
433440
limbs=self._limbs(w), name=vn, val=self._makeini(w, w.val)))
434441
elif isinstance(w, Register):
442+
rval = self._regmap.get(w, w.reset_value)
443+
if rval is None:
444+
rval = self.default_value
435445
write('static uint64_t {name}[{limbs}] = {val};'.format(
436-
limbs=self._limbs(w), name=vn,
437-
val=self._makeini(w, self._regmap.get(w, self.default_value))))
446+
limbs=self._limbs(w), name=vn, val=self._makeini(w, rval)))
438447
else:
439448
write('uint64_t {name}[{limbs}];'.format(limbs=self._limbs(w), name=vn))
440449

pyrtl/importexport.py

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def SLiteral(x):
157157
dffas_def = Group(SKeyword('.subckt') + dffas_keyword + dffas_formal)('dffas_def')
158158

159159
# synchronous Flip-flop
160-
dffs_init_val = Optional(oneOf("0 1 2 3"), default=Literal("0"))
160+
dffs_init_val = Optional(oneOf('0 1 2 3'), default='0')
161161
# TODO I think <type> and <control> ('re' and 'C') below are technically optional too
162162
dffs_def = Group(SKeyword('.latch')
163163
+ signal_id('D')
@@ -345,19 +345,14 @@ def twire(w):
345345
ff_clk_set.add(command['C'])
346346

347347
# Create register and assign next state to D and output to Q
348+
# We ignore initialization values of 2 (don't care) and 3 (unknown).
348349
regname = command['Q'] + '_reg'
349-
flop = Register(bitwidth=1)
350+
init_val = command['I']
351+
rval = {'0': 0, '1': 1, '2': None, '3': None}[init_val]
352+
flop = Register(bitwidth=1, reset_value=rval)
350353
subckt.add_reg(regname, flop)
351354
flop.next <<= twire(command['D'])
352355
flop_output = twire(command['Q'])
353-
init_val = command['I']
354-
if init_val == "1":
355-
# e.g. in Verilog: `initial reg <= 1;`
356-
raise PyrtlError("Initializing latches to 1 is not supported. "
357-
"Acceptable values are: 0, 2 (don't care), and 3 (unknown); "
358-
"in any case, PyRTL will ensure all stateful elements come up 0. "
359-
"For finer control over the initial value, use specialized reset "
360-
"logic.")
361356
flop_output <<= flop
362357

363358
def extract_model_reference(parent, command):
@@ -416,8 +411,22 @@ def instantiate(subckt):
416411
#
417412

418413

419-
def output_to_verilog(dest_file, block=None):
420-
""" A function to walk the block and output it in verilog format to the open file. """
414+
def output_to_verilog(dest_file, add_reset=True, block=None):
415+
""" A function to walk the block and output it in Verilog format to the open file.
416+
417+
:param dest_file: Open file where the Verilog output will be written
418+
:param add_reset: If reset logic should be added. Allowable options are:
419+
False (meaning no reset logic is added), True (default, for adding synchronous
420+
reset logic), and 'asynchronous' (for adding asynchronous reset logic).
421+
:param block: Block to be walked and exported
422+
423+
The registers will be set to their reset_value, if specified, otherwise 0.
424+
"""
425+
426+
if not isinstance(add_reset, bool):
427+
if add_reset != 'asynchronous':
428+
raise PyrtlError("Invalid add_reset option %s. Acceptable options are "
429+
"False, True, and 'asynchronous'")
421430

422431
block = working_block(block)
423432
file = dest_file
@@ -429,15 +438,15 @@ def output_to_verilog(dest_file, block=None):
429438
def varname(wire):
430439
return internal_names[wire.name]
431440

432-
_to_verilog_header(file, block, varname)
441+
_to_verilog_header(file, block, varname, add_reset)
433442
_to_verilog_combinational(file, block, varname)
434-
_to_verilog_sequential(file, block, varname)
443+
_to_verilog_sequential(file, block, varname, add_reset)
435444
_to_verilog_memories(file, block, varname)
436445
_to_verilog_footer(file)
437446

438447

439448
def OutputToVerilog(dest_file, block=None):
440-
""" A deprecated function to output verilog, use "output_to_verilog" instead. """
449+
""" A deprecated function to output Verilog, use "output_to_verilog" instead. """
441450
return output_to_verilog(dest_file, block)
442451

443452

@@ -487,7 +496,7 @@ def _verilog_block_parts(block):
487496
return inputs, outputs, registers, wires, memories
488497

489498

490-
def _to_verilog_header(file, block, varname):
499+
def _to_verilog_header(file, block, varname, add_reset):
491500
""" Print the header of the verilog implementation. """
492501

493502
def name_sorted(wires):
@@ -504,13 +513,17 @@ def name_list(wires):
504513

505514
# module name
506515
io_list = ['clk'] + name_list(name_sorted(inputs)) + name_list(name_sorted(outputs))
516+
if add_reset:
517+
io_list.insert(1, 'rst')
507518
if any(w.startswith('tmp') for w in io_list):
508519
raise PyrtlError('input or output with name starting with "tmp" indicates unnamed IO')
509520
io_list_str = ', '.join(io_list)
510521
print('module toplevel({:s});'.format(io_list_str), file=file)
511522

512523
# inputs and outputs
513524
print(' input clk;', file=file)
525+
if add_reset:
526+
print(' input rst;', file=file)
514527
for w in name_sorted(inputs):
515528
print(' input{:s} {:s};'.format(_verilog_vector_decl(w), varname(w)), file=file)
516529
for w in name_sorted(outputs):
@@ -596,18 +609,33 @@ def name_sorted(wires):
596609
print('', file=file)
597610

598611

599-
def _to_verilog_sequential(file, block, varname):
612+
def _to_verilog_sequential(file, block, varname, add_reset):
600613
""" Print the sequential logic of the verilog implementation. """
601614
if not block.logic_subset(op='r'):
602615
return
603616

604617
print(' // Registers', file=file)
605-
print(' always @( posedge clk )', file=file)
618+
if add_reset == 'asynchronous':
619+
print(' always @(posedge clk or posedge rst)', file=file)
620+
else:
621+
print(' always @(posedge clk)', file=file)
606622
print(' begin', file=file)
623+
if add_reset:
624+
print(' if (rst) begin', file=file)
625+
for net in _net_sorted(block.logic, varname):
626+
if net.op == 'r':
627+
dest = varname(net.dests[0])
628+
rval = net.dests[0].reset_value
629+
if rval is None:
630+
rval = 0
631+
print(' {:s} <= {:d};'.format(dest, rval), file=file)
632+
print(' end', file=file)
633+
print(' else begin', file=file)
607634
for net in _net_sorted(block.logic, varname):
608635
if net.op == 'r':
609636
dest, src = (varname(net.dests[0]), varname(net.args[0]))
610-
print(' {:s} <= {:s};'.format(dest, src), file=file)
637+
print(' {:s} <= {:s};'.format(dest, src), file=file)
638+
print(' end', file=file)
611639
print(' end', file=file)
612640
print('', file=file)
613641

@@ -617,7 +645,7 @@ def _to_verilog_memories(file, block, varname):
617645
memories = {n.op_param[1] for n in block.logic_subset('m@')}
618646
for m in sorted(memories, key=lambda m: m.id):
619647
print(' // Memory mem_{}: {}'.format(m.id, m.name), file=file)
620-
print(' always @( posedge clk )', file=file)
648+
print(' always @(posedge clk)', file=file)
621649
print(' begin', file=file)
622650
for net in _net_sorted(block.logic_subset('@'), varname):
623651
if net.op_param[1] == m:
@@ -692,7 +720,16 @@ def name_list(wires):
692720

693721
def init_regvalue(r):
694722
if simulation_trace:
695-
return simulation_trace.init_regvalue.get(r, simulation_trace.default_value)
723+
rval = simulation_trace.init_regvalue.get(r)
724+
# Currently, the simulation stores the initial value for all registers
725+
# in init_regvalue, so rval should not be None at this point.
726+
# For the strange case where the trace was made by hand/other special use
727+
# cases, check it against None anyway.
728+
if rval is None:
729+
rval = r.reset_value
730+
if rval is None:
731+
rval = simulation_trace.default_value
732+
return rval
696733
else:
697734
return 0
698735

pyrtl/simulation.py

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class Simulation(object):
5252
}
5353

5454
def __init__(
55-
self, tracer=True, register_value_map=None, memory_value_map=None,
55+
self, tracer=True, register_value_map={}, memory_value_map={},
5656
default_value=0, block=None):
5757
""" Creates a new circuit simulator.
5858
@@ -61,14 +61,15 @@ def __init__(
6161
passed, no tracer is instantiated (which is good for long running simulations).
6262
If the default (true) is passed, Simulation will create a new tracer automatically
6363
which can be referenced by the member variable .tracer
64-
:param register_value_map: Defines the initial value for
65-
the registers specified. Format: {Register: value}.
64+
:param register_value_map: Defines the initial value for the registers specified;
65+
overrides the registers's reset_value. Format: {Register: value}.
6666
:param memory_value_map: Defines initial values for many
6767
addresses in a single or multiple memory. Format: {Memory: {address: Value}}.
6868
Memory is a memory block, address is the address of a value
69-
:param default_value: is the value that all unspecified registers and
70-
memories will initialize to. If no default_value is specified, it will
71-
use the value stored in the object (default to 0)
69+
:param default_value: The value that all unspecified registers and
70+
memories will initialize to (default 0). For registers, this is the value that
71+
will be used if the particular register doesn't have a specified reset_value,
72+
and isn't found in the register_value_map.
7273
:param block: the hardware block to be traced (which might be of type PostSynthesisBlock).
7374
defaults to the working block
7475
@@ -94,20 +95,23 @@ def __init__(
9495
self.tracer = tracer
9596
self._initialize(register_value_map, memory_value_map)
9697

97-
def _initialize(self, register_value_map=None, memory_value_map=None):
98+
def _initialize(self, register_value_map={}, memory_value_map={}):
9899
""" Sets the wire, register, and memory values to default or as specified.
99100
100101
:param register_value_map: is a map of {Register: value}.
101102
:param memory_value_map: is a map of maps {Memory: {address: Value}}.
102-
:param default_value: is the value that all unspecified registers and memories will
103-
default to. If no default_value is specified, it will use the value stored in the
104-
object (default to 0)
103+
:param default_value: is the value that all unspecified registers and
104+
memories will initialize to (default 0). For registers, this is the value that
105+
will be used if the particular register doesn't have a specified reset_value,
106+
and isn't found in the register_value_map.
105107
"""
106108
# set registers to their values
107109
reg_set = self.block.wirevector_subset(Register)
108-
if register_value_map is not None:
109-
for r in reg_set:
110-
self.value[r] = self.regvalue[r] = register_value_map.get(r, self.default_value)
110+
for r in reg_set:
111+
rval = register_value_map.get(r, r.reset_value)
112+
if rval is None:
113+
rval = self.default_value
114+
self.value[r] = self.regvalue[r] = rval
111115

112116
# set constants to their set values
113117
for w in self.block.wirevector_subset(Const):
@@ -120,21 +124,20 @@ def _initialize(self, register_value_map=None, memory_value_map=None):
120124
if memid not in self.memvalue:
121125
self.memvalue[memid] = {}
122126

123-
if memory_value_map is not None:
124-
for (mem, mem_map) in memory_value_map.items():
125-
if isinstance(mem, RomBlock):
126-
raise PyrtlError('error, one or more of the memories in the map is a RomBlock')
127-
if isinstance(self.block, PostSynthBlock):
128-
mem = self.block.mem_map[mem] # pylint: disable=maybe-no-member
129-
self.memvalue[mem.id] = mem_map
130-
max_addr_val, max_bit_val = 2**mem.addrwidth, 2**mem.bitwidth
131-
for (addr, val) in mem_map.items():
132-
if addr < 0 or addr >= max_addr_val:
133-
raise PyrtlError('error, address %s in %s outside of bounds' %
134-
(str(addr), mem.name))
135-
if val < 0 or val >= max_bit_val:
136-
raise PyrtlError('error, %s at %s in %s outside of bounds' %
137-
(str(val), str(addr), mem.name))
127+
for (mem, mem_map) in memory_value_map.items():
128+
if isinstance(mem, RomBlock):
129+
raise PyrtlError('error, one or more of the memories in the map is a RomBlock')
130+
if isinstance(self.block, PostSynthBlock):
131+
mem = self.block.mem_map[mem] # pylint: disable=maybe-no-member
132+
self.memvalue[mem.id] = mem_map
133+
max_addr_val, max_bit_val = 2**mem.addrwidth, 2**mem.bitwidth
134+
for (addr, val) in mem_map.items():
135+
if addr < 0 or addr >= max_addr_val:
136+
raise PyrtlError('error, address %s in %s outside of bounds' %
137+
(str(addr), mem.name))
138+
if val < 0 or val >= max_bit_val:
139+
raise PyrtlError('error, %s at %s in %s outside of bounds' %
140+
(str(val), str(addr), mem.name))
138141

139142
# set all other variables to default value
140143
for w in self.block.wirevector_set:
@@ -437,7 +440,7 @@ class FastSimulation(object):
437440
# when put into the generated code
438441

439442
def __init__(
440-
self, register_value_map=None, memory_value_map=None,
443+
self, register_value_map={}, memory_value_map={},
441444
default_value=0, tracer=True, block=None, code_file=None):
442445
""" Instantiates a Fast Simulation instance.
443446
@@ -469,17 +472,17 @@ def __init__(
469472
self.internal_names = _PythonSanitizer('_fastsim_tmp_')
470473
self._initialize(register_value_map, memory_value_map)
471474

472-
def _initialize(self, register_value_map=None, memory_value_map=None):
473-
if register_value_map is None:
474-
register_value_map = {}
475-
475+
def _initialize(self, register_value_map={}, memory_value_map={}):
476476
for wire in self.block.wirevector_set:
477477
self.internal_names.make_valid_string(wire.name)
478478

479479
# set registers to their values
480480
reg_set = self.block.wirevector_subset(Register)
481481
for r in reg_set:
482-
self.regs[r.name] = register_value_map.get(r, self.default_value)
482+
rval = register_value_map.get(r, r.reset_value)
483+
if rval is None:
484+
rval = self.default_value
485+
self.regs[r.name] = rval
483486

484487
self._initialize_mems(memory_value_map)
485488

@@ -497,12 +500,11 @@ def _initialize(self, register_value_map=None, memory_value_map=None):
497500
self.sim_func = context['sim_func']
498501

499502
def _initialize_mems(self, memory_value_map):
500-
if memory_value_map is not None:
501-
for (mem, mem_map) in memory_value_map.items():
502-
if isinstance(mem, RomBlock):
503-
raise PyrtlError('error, one or more of the memories in the map is a RomBlock')
504-
name = self._mem_varname(mem)
505-
self.mems[name] = mem_map
503+
for (mem, mem_map) in memory_value_map.items():
504+
if isinstance(mem, RomBlock):
505+
raise PyrtlError('error, one or more of the memories in the map is a RomBlock')
506+
name = self._mem_varname(mem)
507+
self.mems[name] = mem_map
506508

507509
for net in self.block.logic_subset('m@'):
508510
mem = net.op_param[1]
@@ -1227,7 +1229,7 @@ def _set_initial_values(self, default_value, init_regvalue, init_memvalue):
12271229
12281230
:param default_value: Default value to be used for all registers and
12291231
memory locations if not found in the other passed in maps
1230-
:param init_regvalue: Default value for registers
1232+
:param init_regvalue: Default value for all the registers
12311233
:param init_memvvalue: Default value for memory locations of given maps
12321234
12331235
This is needed when using this trace for outputting a Verilog testbench,

pyrtl/wire.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,9 +706,33 @@ def __init__(self, rhs, is_conditional):
706706
self.rhs = rhs
707707
self.is_conditional = is_conditional
708708

709-
def __init__(self, bitwidth, name='', block=None):
709+
def __init__(self, bitwidth, name='', reset_value=None, block=None):
710+
""" Construct a register.
711+
712+
:param int bitwidth: Number of bits to represent this register.
713+
:param String name: The name of the wire. Must be unique. If none
714+
is provided, one will be autogenerated.
715+
:param reset_value: Value to initialize this register to during simulation
716+
and in any code (e.g. Verilog) that is exported. Defaults to 0, but can
717+
be explicitly overridden at simulation time.
718+
:param block: The block under which the wire should be placed.
719+
Defaults to the working block.
720+
:return: a WireVector object representing a register.
721+
722+
It is an error if the reset_value cannot fit into the specified bitwidth
723+
for this register.
724+
"""
725+
from pyrtl.helperfuncs import infer_val_and_bitwidth
726+
710727
super(Register, self).__init__(bitwidth=bitwidth, name=name, block=block)
711728
self.reg_in = None # wire vector setting self.next
729+
if reset_value is not None:
730+
reset_value, rst_bitwidth = infer_val_and_bitwidth(reset_value)
731+
if rst_bitwidth > bitwidth:
732+
raise PyrtlError(
733+
'reset_value "%s" cannot fit in the specified %d bits for this register'
734+
% (str(reset_value), bitwidth))
735+
self.reset_value = reset_value
712736

713737
@property
714738
def next(self):

0 commit comments

Comments
 (0)