def test_default_value_for_wires(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r1 = pyrtl.Register(bitwidth=3, name='r1') r2 = pyrtl.Register(bitwidth=3, name='r2') r3 = pyrtl.Register(bitwidth=3, name='r3') o = pyrtl.Output(bitwidth=3, name='o') with pyrtl.conditional_assignment(defaults={r1: r1 + 2, r2: 6, o: 3}): with i < 2: r1.next |= r1 + 1 # r2 will be updated to 6 # r3 remains the same as previous cycle o |= i with i < 3: # r1 will be updated to r1 + 2 r2.next |= r2 + 1 # r3 remains the same as previous cycle # o will be updated to 3 with pyrtl.otherwise: # r1 will be updated to r1 + 2 # r2 will be updated to 6 r3.next |= 2 o |= 7 self.check_trace(' i 01230123\n' ' o 01370137\n' 'r1 01246702\n' 'r2 06676667\n' 'r3 00002222\n')
def simple_mult(A, B, start): """ Generate simple shift-and-add multiplier. Builds a slow, small multiplier using the simple shift-and-add algorithm. Requires very small area (it uses only a single adder), but has long delay (worst case is len(a) cycles). a and b are arbitrary-length inputs; start is a one-bit input to indicate inputs are ready.done is a one-bit signal output raised when the multiplication is finished, at which point the product will be on the result line (returned by the function). """ alen = len(A) blen = len(B) areg = pyrtl.Register(alen) breg = pyrtl.Register(blen + alen) accum = pyrtl.Register(blen + alen) done = areg == 0 # Multiplication is finished when a becomes 0 # During multiplication, shift a right every cycle, b left every cycle with pyrtl.conditional_assignment: with start: # initialization areg.next |= A breg.next |= B accum.next |= 0 with ~done: # don't run when there's no work to do areg.next |= areg[1:] # right shift breg.next |= pyrtl.concat(breg, "1'b0") # left shift # "Multply" shifted breg by LSB of areg by conditionally adding with areg[0]: accum.next |= accum + breg # adds to accum only when LSB of areg is 1 return accum, done
def attempt2_hardware_fibonacci(n, bitwidth): a = pyrtl.Register(bitwidth, 'a') b = pyrtl.Register(bitwidth, 'b') a.next <<= b b.next <<= a + b return a
def nrml(din, offset=24): zero = pyrtl.Const(0, 1) one = pyrtl.Const(1, 1) temp = pyrtl.Register(32, name='temp') temp.next <<= barrel_shifter(din, zero, zero, offset) dout = pyrtl.Register(8, name='dout') dout.next <<= temp[:8] return dout
def decryption_statem(self, ciphertext_in, key_in, reset): """ Builds a multiple cycle AES Decryption state machine circuit :param reset: a one bit signal telling the state machine to reset and accept the current plaintext and key :return ready, plain_text: ready is a one bit signal showing that the decryption result (plain_text) has been calculated. """ if len(key_in) != len(ciphertext_in): raise pyrtl.PyrtlError( "AES key and ciphertext should be the same length") cipher_text, key = (pyrtl.Register(len(ciphertext_in)) for i in range(2)) key_exp_in, add_round_in = (pyrtl.WireVector(len(ciphertext_in)) for i in range(2)) # this is not part of the state machine as we need the keys in # reverse order... reversed_key_list = reversed(self._key_gen(key_exp_in)) counter = pyrtl.Register(4, 'counter') round = pyrtl.WireVector(4) counter.next <<= round inv_shift = self._inv_shift_rows(cipher_text) inv_sub = self._sub_bytes(inv_shift, True) key_out = pyrtl.mux(round, *reversed_key_list, default=0) add_round_out = self._add_round_key(add_round_in, key_out) inv_mix_out = self._mix_columns(add_round_out, True) with pyrtl.conditional_assignment: with reset == 1: round |= 0 key.next |= key_in key_exp_in |= key_in # to lower the number of cycles needed cipher_text.next |= add_round_out add_round_in |= ciphertext_in with counter == 10: # keep everything the same round |= counter cipher_text.next |= cipher_text with pyrtl.otherwise: # running through AES round |= counter + 1 key.next |= key key_exp_in |= key add_round_in |= inv_sub with counter == 9: cipher_text.next |= add_round_out with pyrtl.otherwise: cipher_text.next |= inv_mix_out ready = (counter == 10) return ready, cipher_text
def setUp(self): pyrtl.reset_working_block() self.i = pyrtl.Input(bitwidth=3) self.r1 = pyrtl.Register(name='r1', bitwidth=3) self.r2 = pyrtl.Register(name='r2', bitwidth=3) self.o = pyrtl.Output(name='o', bitwidth=3) self.r1.next <<= self.i self.r2.next <<= self.r1 self.o <<= self.r2
def test_basic_two_conditions(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r = pyrtl.Register(bitwidth=2, name='r') with pyrtl.conditional_assignment: with i == 2: r.next |= r + 1 with i == 3: r.next |= r - 1 self.check_trace('i 01230123\nr 00010001\n')
def attempt3_hardware_fibonacci(n, bitwidth): a = pyrtl.Register(bitwidth, 'a') b = pyrtl.Register(bitwidth, 'b') i = pyrtl.Register(bitwidth, 'i') i.next <<= i + 1 a.next <<= b b.next <<= a + b return a, i == n
def rcoinc(a, b): last_a = pyrtl.Register(bitwidth=1) last_b = pyrtl.Register(bitwidth=1) holdit = pyrtl.Register(bitwidth=1) last_a.next <<= a last_b.next <<= b coinc_instant = a & b & (~last_a) & (~last_b) coinc = holdit | coinc_instant holdit.next <<= coinc return coinc
def test_two_seperate_conditions(self): c = pyrtl.Const(1) i = pyrtl.Register(bitwidth=2, name='i') r = pyrtl.Register(bitwidth=2, name='r') with pyrtl.conditional_assignment: with c: i.next |= i + 1 with pyrtl.conditional_assignment: with i == 2: r.next |= r + 1 self.check_trace('i 01230123\nr 00011112\n')
def test_nested_under_default_condition(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r = pyrtl.Register(bitwidth=3, name='r') with pyrtl.conditional_assignment: with i < 2: r.next |= r + 2 with pyrtl.otherwise: with r < 6: r.next |= r - 1 self.check_trace('i 01230123\nr 02432466\n')
def test_basic_nested_condition(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r = pyrtl.Register(bitwidth=3, name='r') with pyrtl.conditional_assignment: with (i == 2) | (i == 3): with r < 3: r.next |= r + 2 with pyrtl.otherwise: r.next |= r + 1 self.check_trace('i 01230123\nr 00024445\n')
def test_basic_default_condition(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r = pyrtl.Register(bitwidth=2, name='r') with pyrtl.conditional_assignment: with i == 2: r.next |= r with i == 3: r.next |= r - 1 with pyrtl.otherwise: r.next |= r + 1 self.check_trace('i 01230123\nr 01221233\n')
def test_basic_nested_non_exclusive_condition(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r1 = pyrtl.Register(bitwidth=3, name='r1') r2 = pyrtl.Register(bitwidth=3, name='r2') with pyrtl.conditional_assignment: with r1 < 3: r1.next |= r1 + 1 with pyrtl.otherwise: pass with r2 < 3: r2.next |= r2 + 1 self.check_trace(' i 01230123\nr1 01233333\nr2 01233333\n')
def test_two_signals_under_default_condition(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r1 = pyrtl.Register(bitwidth=3, name='r1') r2 = pyrtl.Register(bitwidth=3, name='r2') with pyrtl.conditional_assignment: with i < 2: r1.next |= r1 + 1 with i < 3: r2.next |= r2 + 1 with pyrtl.otherwise: r2.next |= 3 self.check_trace(' i 01230123\nr1 01222344\nr2 00013334\n')
def test_overlaping_assignments_in_non_exclusive_assignments(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r1 = pyrtl.Register(bitwidth=3, name='r1') r2 = pyrtl.Register(bitwidth=3, name='r2') with self.assertRaises(pyrtl.PyrtlError): with pyrtl.conditional_assignment: with r1 < 3: r1.next |= r1 + 1 with pyrtl.otherwise: pass with r2 < 3: r1.next |= r2 + 1
def test_one_deep_nested_non_exclusive_condition(self): i = pyrtl.Register(bitwidth=2, name='i') i.next <<= i + 1 r1 = pyrtl.Register(bitwidth=3, name='r1') r2 = pyrtl.Register(bitwidth=3, name='r2') with pyrtl.conditional_assignment: with (i == 2) | (i == 3): with r1 < 3: r1.next |= r1 + 2 with pyrtl.otherwise: pass with r2 < 3: r2.next |= r2 + 2 self.check_trace(' i 01230123\nr1 00024444\nr2 00024444\n')
def encrypt_state_m(self, plaintext_in, key_in, reset): """ Builds a multiple cycle AES Encryption state machine circuit :param reset: a one bit signal telling the state machine to reset and accept the current plaintext and key :return ready, cipher_text: ready is a one bit signal showing that the encryption result (cipher_text) has been calculated. """ if len(key_in) != len(plaintext_in): raise pyrtl.PyrtlError( "AES key and plaintext should be the same length") plain_text, key = (pyrtl.Register(len(plaintext_in)) for i in range(2)) key_exp_in, add_round_in = (pyrtl.WireVector(len(plaintext_in)) for i in range(2)) counter = pyrtl.Register(4, 'counter') round = pyrtl.WireVector(4, 'round') counter.next <<= round sub_out = self._sub_bytes(plain_text) shift_out = self._shift_rows(sub_out) mix_out = self._mix_columns(shift_out) key_out = self._key_expansion(key, counter) add_round_out = self._add_round_key(add_round_in, key_exp_in) with pyrtl.conditional_assignment: with reset == 1: round |= 0 key_exp_in |= key_in # to lower the number of cycles plain_text.next |= add_round_out key.next |= key_in add_round_in |= plaintext_in with counter == 10: # keep everything the same round |= counter plain_text.next |= plain_text with pyrtl.otherwise: # running through AES round |= counter + 1 key_exp_in |= key_out plain_text.next |= add_round_out key.next |= key_out with counter == 9: add_round_in |= shift_out with pyrtl.otherwise: add_round_in |= mix_out ready = (counter == 10) return ready, plain_text
def __init__(self, depth_width=2, data_width=32): aw = depth_width dw = data_width self.wr_data_i = pyrtl.Input(dw, 'wr_data_i') self.wr_en_i = pyrtl.Input(1, 'wr_en_i') self.rd_data_o = pyrtl.Output(dw, 'rd_data_o') self.rd_en_i = pyrtl.Input(1, 'rd_en_i') self.full_o = pyrtl.Output(1, 'full_o') self.empty_o = pyrtl.Output(1, 'empty_o') self.one_left = pyrtl.Output(1, 'one_left') self.reset = pyrtl.Input(1, 'reset') self.write_pointer = pyrtl.Register(aw + 1, 'write_pointer') self.read_pointer = pyrtl.Register(aw + 1, 'read_pointer') self.read_plus_1 = pyrtl.Const(1, 1) + self.read_pointer self.read_pointer.next <<= pyrtl.select(self.rd_en_i, truecase=self.read_plus_1, falsecase=self.read_pointer) self.write_pointer.next <<= pyrtl.select( self.reset, truecase=pyrtl.Const(0, aw + 1), falsecase=pyrtl.select(self.wr_en_i, truecase=self.write_pointer + 1, falsecase=self.write_pointer)) self.empty_int = pyrtl.WireVector(1, 'empty_int') self.full_or_empty = pyrtl.WireVector(1, 'full_or_empty') self.empty_int <<= self.write_pointer[aw] == self.read_pointer[aw] self.full_or_empty <<= self.write_pointer[0:aw] == self.read_pointer[ 0:aw] self.full_o <<= self.full_or_empty & ~self.empty_int self.empty_o <<= self.full_or_empty & self.empty_int self.one_left <<= (self.read_pointer + 1) == self.write_pointer self.mem = m = _RAM(num_entries=1 << aw, data_nbits=dw, name='FIFOStorage') m.wen <<= self.wr_en_i m.ren <<= self.rd_en_i m.raddr <<= self.read_pointer[0:aw] m.waddr <<= self.write_pointer[0:aw] m.wdata <<= self.wr_data_i self.rd_data_o <<= pyrtl.select(self.rd_en_i, truecase=m.rdata, falsecase=pyrtl.Const(0, dw))
def setUp(self): pyrtl.reset_working_block() bitwidth = 3 self.r = pyrtl.Register(bitwidth=bitwidth, name='r') self.result = _basic_add(self.r, pyrtl.Const(1).zero_extended(bitwidth)) self.r.next <<= self.result
def test_default_value_for_registers_without_reset_value(self): r = pyrtl.Register(2, name='r', reset_value=3) s = pyrtl.Register(2, name='s') r.next <<= r + 1 s.next <<= s - 1 o = pyrtl.Output(4, 'o') o <<= r + s sim_trace = pyrtl.SimulationTrace() # Should set default value for s only (since r has specified 'reset_value') sim = self.sim(tracer=sim_trace, default_value=3) sim.step_multiple(nsteps=7) output = six.StringIO() sim_trace.print_trace(output, compact=True) self.assertEqual(output.getvalue(), 'o 6222622\nr 3012301\ns 3210321\n')
def test_weird_wire_names(self): """ Some simulations need to be careful when handling special names (eg Fastsim June 2016) """ i = pyrtl.Input(8, '"182&!!!\n') o = pyrtl.Output(8, '*^*)#*$\'*') o2 = pyrtl.Output(8, 'test@+') w = pyrtl.WireVector(8, '[][[-=--09888') r = pyrtl.Register(8, '&@#)^#@^&(asdfkhafkjh') w <<= i r.next <<= i o <<= w o2 <<= r trace = pyrtl.SimulationTrace() sim = self.sim(tracer=trace) sim.step({i: 28}) self.assertEqual(sim.inspect(o), 28) self.assertEqual(sim.inspect(o.name), 28) self.assertEqual(trace.trace[o.name], [28]) sim.step({i: 233}) self.assertEqual(sim.inspect(o), 233) self.assertEqual(sim.inspect(o2), 28) self.assertEqual(sim.inspect(o2.name), 28) self.assertEqual(trace.trace[o2.name], [0, 28])
def test_time_est_unchanged(self): a = pyrtl.Const(2, 8) b = pyrtl.Const(85, 8) zero = pyrtl.Const(0, 1) reg = pyrtl.Register(8) mem = pyrtl.MemBlock(8, 8) out = pyrtl.Output(8) nota, aLSB, athenb, aORb, aANDb, aNANDb, \ aXORb, aequalsb, altb, agtb, aselectb, \ aplusb, bminusa, atimesb, memread = [pyrtl.Output() for i in range(15)] out <<= zero nota <<= ~a aLSB <<= a[0] athenb <<= pyrtl.concat(a, b) aORb <<= a | b aANDb <<= a & b aNANDb <<= a.nand(b) aXORb <<= a ^ b aequalsb <<= a == b altb <<= a < b agtb <<= a > b aselectb <<= pyrtl.select(zero, a, b) reg.next <<= a aplusb <<= a + b bminusa <<= a - b atimesb <<= a * b memread <<= mem[0] mem[1] <<= a timing = estimate.TimingAnalysis() self.assertEqual(timing.max_freq(), 610.2770657878676) self.assertEquals(timing.max_length(), 1255.6000000000001)
def test_area_est_unchanged(self): a = pyrtl.Const(2, 8) b = pyrtl.Const(85, 8) zero = pyrtl.Const(0, 1) reg = pyrtl.Register(8) mem = pyrtl.MemBlock(8, 8) out = pyrtl.Output(8) nota, aLSB, athenb, aORb, aANDb, aNANDb, \ aXORb, aequalsb, altb, agtb, aselectb, \ aplusb, bminusa, atimesb, memread = [pyrtl.Output() for i in range(15)] out <<= zero nota <<= ~a aLSB <<= a[0] athenb <<= pyrtl.concat(a, b) aORb <<= a | b aANDb <<= a & b aNANDb <<= a.nand(b) aXORb <<= a ^ b aequalsb <<= a==b altb <<= a < b agtb <<= a > b aselectb <<= pyrtl.select(zero, a, b) reg.next <<= a aplusb <<= a + b bminusa <<= a - b atimesb <<= a*b memread <<= mem[0] mem[1] <<= a self.assertEquals(estimate.area_estimation(), (0.00734386752, 0.01879779717361501))
def test_trace_to_html_repr_per_name_enum_is_bool(self): from enum import IntEnum class Foo(IntEnum): A = 0 B = 1 i = pyrtl.Input(2, 'i') state = pyrtl.Register(max(Foo).bit_length(), name='state') o = pyrtl.Output(name='o') o <<= state with pyrtl.conditional_assignment: with i == 0b01: state.next |= Foo.A with i == 0b10: state.next |= Foo.B sim = pyrtl.Simulation() sim.step_multiple({'i': [1, 2, 1, 2, 2]}) htmlstring = pyrtl.trace_to_html(sim.tracer, repr_per_name={'state': Foo}) expected = ( '<script type="WaveDrom">\n' '{\n' ' signal : [\n' ' { name: "i", wave: "====.", data: ["0x1", "0x2", "0x1", "0x2"] },\n' ' { name: "o", wave: "0.101" },\n' ' { name: "state", wave: "=.===", data: ["Foo.A", "Foo.B", "Foo.A", "Foo.B"] },\n' ' ],\n' ' config: { hscale: 2 }\n' '}\n' '</script>\n') self.assertEqual(htmlstring, expected)
def test_reg_to_reg_simulation(self): self.r2 = pyrtl.Register(bitwidth=self.bitwidth, name='r2') self.r.next <<= self.r2 self.r2.next <<= self.r + pyrtl.Const(2, bitwidth=self.bitwidth) self.o2 = pyrtl.Output(bitwidth=self.bitwidth, name='o2') self.o2 <<= self.r2 self.check_trace(' o 00224466\no2 02244660\n')
def create_and_check_bundle(self, bundler): w = pyrtl.Bundle(bundler) assert isinstance(w, pyrtl.WireVector) assert hasattr(w, 'funct7') assert hasattr(w, 'rs2') assert hasattr(w, 'rs1') assert hasattr(w, 'funct3') assert hasattr(w, 'rd') assert hasattr(w, 'opcode') assert len(w) == 32 assert len(w.funct7) == 7 assert len(w.rs2) == 5 assert len(w.rs1) == 5 assert len(w.funct3) == 3 assert len(w.rd) == 5 assert len(w.opcode) == 7 r = pyrtl.Register(len(w)) r.next <<= w y = r.as_bundle(bundler) assert hasattr(y, 'funct7') assert hasattr(y, 'rs2') assert hasattr(y, 'rs1') assert hasattr(y, 'funct3') assert hasattr(y, 'rd') assert hasattr(y, 'opcode') assert len(y) == 32 assert len(y.funct7) == 7 assert len(y.rs2) == 5 assert len(y.rs1) == 5 assert len(y.funct3) == 3 assert len(y.rd) == 5 assert len(y.opcode) == 7
def fib(first, second): a = pyrtl.Register(bitwidth=32, name='a') b = pyrtl.Register(bitwidth=32, name='b') return_val = pyrtl.WireVector(bitwidth=32, name='return_val') with pyrtl.conditional_assignment: with a == 0: with b == 0: a.next |= first b.next |= second return_val |= first with pyrtl.otherwise: a.next |= b b.next |= a + b return_val |= b return return_val
def test_all_does_simulation_correct(self): r = pyrtl.Register(3, 'r') r.next <<= r + 1 a, b, c = r[0], r[1], r[2] o = pyrtl.Output(name='o') o <<= pyrtl.corecircuits.rtl_all(a, b, c) self.check_trace('o 00000001\nr 01234567\n')
def test_basic_false_condition(self): c = pyrtl.Const(0) r = pyrtl.Register(bitwidth=2, name='r') with pyrtl.conditional_assignment: with c: r.next |= r + 1 self.check_trace('r 00000000\n')