cin: random.choice([0, 1]) }) sim_trace.render_trace(symbol_len=5, segment_size=5) # ---- Exporting to Verilog ---- # However, not only do we want to have a method to import from Verilog, we also # want a way to export it back out to Verilog as well. To demonstrate PyRTL's # ability to export in Verilog, we will create a sample 3-bit counter. However # unlike the example in example2, we extend it to be synchronously resetting. pyrtl.reset_working_block() zero = pyrtl.Input(1, 'zero') counter_output = pyrtl.Output(3, 'counter_output') counter = pyrtl.Register(3, 'counter') counter.next <<= pyrtl.mux(zero, counter + 1, 0) counter_output <<= counter # The counter gets 0 in the next cycle if the "zero" signal goes high, otherwise just # counter + 1. Note that both "0" and "1" are bit extended to the proper length and # here we are making use of that native add operation. Let's dump this bad boy out # to a verilog file and see what is looks like (here we are using StringIO just to # print it to a string for demo purposes, most likely you will want to pass a normal # open file). print("--- PyRTL Representation ---") print(pyrtl.working_block()) print()
def setUp(self): pyrtl.reset_working_block() self.bitwidth = 3 self.r = pyrtl.Register(bitwidth=self.bitwidth) self.output = pyrtl.Output(bitwidth=self.bitwidth, name='r') self.output <<= self.r
def setUp(self): pyrtl.reset_working_block() self.output = pyrtl.Output(name='r')
# Our goal in this exercise is to implement a simplified 1-bit ALU # that has 2 data inputs: {a, b}, 2 outputs: {r, cout}, # and compute one of the following 3 functions: r = a and b, # r = a xnor b, and cout, r = a + b import pyrtl # Declare two 1-bit data inputs: a, b a = pyrtl.Input(bitwidth=1, name='a') b = pyrtl.Input(bitwidth=1, name='b') # Declare two 1-bit outputs: r, cout r = pyrtl.Output(bitwidth=1, name='r') cout = pyrtl.Output(bitwidth=1, name='cout') # Declare control inputs op = pyrtl.Input(bitwidth=2, name='op') def half_adder(a, b): """ ha_carry_out, ha_sum = a + b """ ha_sum = a ^ b ha_carry_out = a & b# < add your code here > return ha_sum, ha_carry_out def alu (a, b, op): """ Implementation of the desired simplified ALU: if op == 0: return a and b
romdata=gm11_data, asynchronous=True) GM13 = pyrtl.RomBlock(bitwidth=8, addrwidth=8, romdata=gm13_data, asynchronous=True) GM14 = pyrtl.RomBlock(bitwidth=8, addrwidth=8, romdata=gm14_data, asynchronous=True) _inv_gal_mult_dict = {9: GM9, 11: GM11, 13: GM13, 14: GM14} # Hardware build. aes_ciphertext = pyrtl.Input(bitwidth=128, name='aes_ciphertext') aes_key = pyrtl.Input(bitwidth=128, name='aes_key') aes_plaintext = pyrtl.Output(bitwidth=128, name='aes_plaintext') aes_plaintext <<= aes_decryption(aes_ciphertext, aes_key) sim_trace = pyrtl.SimulationTrace( wirevector_subset=[aes_ciphertext, aes_key, aes_plaintext]) sim = pyrtl.Simulation(tracer=sim_trace) for cycle in range(1): sim.step({ aes_ciphertext: 0x66e94bd4ef8a2c3b884cfa59ca342b2e, aes_key: 0x0 }) sim_trace.render_trace(symbol_len=40, segment_size=1)
def coinc(a, b, d): assert isinstance(d, int) assert (d >= 0) tmp1 = min(a, b) tmp2 = max(a, b) tmp3 = delta(tmp1, d) out = inhibit(tmp2, tmp3) return out # hardware in1, in2 = (pyrtl.Input(1, "in" + str(x)) for x in range(1, 3)) out = pyrtl.Output(1, "out") out <<= coinc(in1, in2, 3) # simulation in1_vals = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] in2_vals = [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for cycle in range(len(in1_vals)): sim.step({'in1': in1_vals[cycle], 'in2': in2_vals[cycle]}) sim_trace.render_trace()
sample_instructions = [201326592, 286326786, 4202528, 2366177284] mem = pyrtl.RomBlock(bitwidth=32, addrwidth=2, romdata=sample_instructions, max_read_ports=1) # variable counter will serve as an address in this example counter = pyrtl.Register(bitwidth=2) counter.next <<= counter + 1 # read data stored in rom data = pyrtl.WireVector(bitwidth=32, name='data') data <<= mem[counter] # output data op = pyrtl.Output(bitwidth=6, name='op') rs = pyrtl.Output(bitwidth=5, name='rs') rt = pyrtl.Output(bitwidth=5, name='rt') rd = pyrtl.Output(bitwidth=5, name='rd') sh = pyrtl.Output(bitwidth=5, name='sh') func = pyrtl.Output(bitwidth=6, name='func') imm = pyrtl.Output(bitwidth=16, name='imm') addr = pyrtl.Output(bitwidth=26, name='addr') ### ADD YOUR INSTRUCTION DECODE LOGIC HERE ### op <<= data[-6:] rs <<= data[21:26] rt <<= data[16:21] rd <<= data[11:16] sh <<= data[6:11] func <<= data[0:6]
### Testing ### # List of input values test_din = [5, 2, 4] # Create input wirevectors and assign the corresponding race values to them sim_dict = {} din_wv = [] for i, val in enumerate(test_din): locals()["din" + str(i)] = pyrtl.Input(bitwidth=1, name=str("din%d" % i)) sim_dict[str("din%d" % i)] = race_testval(val).next din_wv.append(locals()["din" + str(i)]) # Create output wirevectors inh_out_case0 = pyrtl.Output(bitwidth=1, name="inh(din1,din0)_out") inh_out_case1 = pyrtl.Output(bitwidth=1, name="inh(din0,din1)_out") max_out = pyrtl.Output(bitwidth=1, name="max_out") min_out = pyrtl.Output(bitwidth=1, name="min_out") add_const_out = pyrtl.Output(bitwidth=1, name="add_const_out") # Call functions inh_out_case0 <<= inhibit_rl(din1, din0) # controlling input: din1 inh_out_case1 <<= inhibit_rl(din0, din1) # controlling input: din0 max_out <<= max_rl(din_wv) min_out <<= min_rl(din_wv) add_const_out <<= add_const_rl(din0, 3) # add k=3 # Simulate sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace)
def setUp(self): pyrtl.reset_working_block() self.in1, self.in2 = (pyrtl.Input(8, "in" + str(i)) for i in range(1, 3)) self.out = pyrtl.Output(9, "out")
import pyrtl a = pyrtl.Input(1, "a") b = pyrtl.Input(1, "b") c = pyrtl.Output(1, "out") wire = a & b c <<= wire d = pyrtl.Output(2, "concat") d <<= pyrtl.concat(a, b) sim = pyrtl.Simulation(tracer=pyrtl.SimulationTrace()) import random sim_ins = {a: 0, b: 1} sim.step(sim_ins) for cycle in range(15): sim.step({ a: random.choice([0, 1]), b: random.choice([0, 1]), # c: random.choice([0, 1]) }) sim.tracer.render_trace(symbol_len=5, segment_size=5)
# -*- coding: utf-8 -*- import pyrtl pyrtl.set_debug_mode(debug=True) DATA_WIDTH = 8 ADDR_WIDTH = 8 RAM_DEPTH = 1 << 8 address = pyrtl.Input(ADDR_WIDTH, "address") cs = pyrtl.Input(1, "cs") we = pyrtl.Input(1, "we") oe = pyrtl.Input(1, "oe") data_in = pyrtl.Input(DATA_WIDTH, "data_in") data_out = pyrtl.Output(DATA_WIDTH, "data_out") mem = pyrtl.memory.MemBlock(RAM_DEPTH, ADDR_WIDTH, name="mem", asynchronous=True) mem[address] <<= pyrtl.MemBlock.EnabledWrite(data_in, we & cs) data_out_wire = pyrtl.wire.WireVector(DATA_WIDTH, "DATA_WIDTH") with pyrtl.conditional_assignment: with cs: with we: pass with pyrtl.otherwise: with oe: data_out_wire |= mem[address]
import pyrtl # "pin" input/outputs a = pyrtl.Input(8, 'a') b = pyrtl.Input(8, 'b') q = pyrtl.Output(8, 'q') gt5 = pyrtl.Output(1, 'gt5') sum = a + b # makes an 8-bit adder q <<= sum # assigns output of adder to out pin gt5 <<= sum > 5 # does a comparison, assigns that to different pin # the simulation and waveform output sim = pyrtl.Simulation() sim.step_multiple({'a': [0, 1, 2, 3, 4], 'b': [2, 2, 3, 3, 4]}) sim.tracer.render_trace()
def setUp(self): pyrtl.reset_working_block() a, b = pyrtl.input_list('a/4 b/4') o = pyrtl.Output(5, 'o') o <<= a + b
def routput(name=None): return pyrtl.Output(bitwidth=1, name=name)
self.BBS[BBSind] = ~branch_outcome & 1 #print("PHT", self.PHT[PHTind]) # Parameters PHT_size = 1024 BBS_size = 4096 PHT_bitwidth = int(math.log(PHT_size, 2)) BBS_bitwidth = int(math.log(BBS_size, 2)) BHR_bitwidth = PHT_bitwidth PHT_mask = pyrtl.Const(PHT_size - 1) BBS_mask = pyrtl.Const(BBS_size - 1) # Define Inputs and Outputs pc_i, outcome_i = pyrtl.Input(32, 'pc'), pyrtl.Input(1, 'outcome') prediction_o = pyrtl.Output(1, 'prediction') # PyRTL description of the Agree Predictor BHR_r = pyrtl.Register(bitwidth=BHR_bitwidth, name='BHR_r') PHT_m = pyrtl.MemBlock(bitwidth=2, addrwidth=PHT_bitwidth, name='PHT_table', asynchronous=True) BBS_m = pyrtl.MemBlock(bitwidth=1, addrwidth=BBS_bitwidth, name='BBS_table', asynchronous=True) pc_shift_right_2bits = pc_i[-30:] PHT_ind = pyrtl.WireVector(bitwidth=PHT_bitwidth, name='PHT_ind') PHT_ind <<= (pc_shift_right_2bits ^ BHR_r) & PHT_mask
#!/usr/bin/env python # -*- coding: utf-8 -*- import pyrtl chance = pyrtl.Input(4, "chance") gene = pyrtl.Input(4, "gene") out = pyrtl.Output(2, "out") mutation_state = pyrtl.Register(2, "mutation_state") NEUTRAL, GOOD, BAD = [pyrtl.Const(x, bitwidth=2) for x in range(3)] with pyrtl.conditional_assignment: with chance == gene: mutation_state.next |= GOOD with chance == gene - 1: mutation_state.next |= BAD with pyrtl.otherwise: mutation_state.next |= NEUTRAL out <<= mutation_state # Setup the simulation sim_trace = pyrtl.SimulationTrace([chance, gene, out]) sim = pyrtl.FastSimulation(tracer=sim_trace)
# 'c': random.choice([0, 1]) # }) reg_vec = [pyrtl.Register(32) for i in range(0, 8)] inputs = [pyrtl.Input(32, 'input_{}'.format(i)) for i in range(0, 8)] test_dict = { 'input_0': 3, 'input_1': -39 & 0xFFFFFFFF, 'input_2': 17, 'input_3': 7, 'input_4': -42 & 0xFFFFFFFF, 'input_5': -18 & 0xFFFFFFFF, 'input_6': 37, 'input_7': -6 & 0xFFFFFFFF } output_orig = [pyrtl.Output(32, 'out_orig_{}'.format(i)) for i in range(0, 8)] output_relu = [pyrtl.Output(32, 'out_relu_{}'.format(i)) for i in range(0, 8)] for index, reg in enumerate(reg_vec): # inputs[index] <<= test_dict[index] reg.next <<= inputs[index] output_orig[index] <<= reg relu_func_out = relu(reg_vec) for i, index in enumerate(output_relu): index <<= relu_func_out[i] sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for cycle in range(35): sim.step(test_dict)
from __future__ import division import pyrtl a, b = pyrtl.Input(1, 'a'), pyrtl.Input(1, 'b') out_a, out_b = (pyrtl.Output(1, 'out_' + i) for i in ("a", "b")) parity = a ^ b out_a <<= parity ^ a out_b <<= parity ^ b trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=trace) for i in range(4): a_in = i % 2 b_in = i // 2 sim.step({a: a_in, b: b_in}) # print("a = {}, b = {}".format(a_in, b_in)) assert(a_in == sim.inspect(out_b)) assert(b_in == sim.inspect(out_a)) trace.render_trace() # b = a ^ b # aka b_inter # out_a = a ^b_inter # out_b = out_a ^ b_inter
import os, sys sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'base')) sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'racetrees')) from flat_racetree import FlatRaceTree from racelogic_sim_input_stimuli import race_testval ### Testing ### inp_res = 4 # input resolution -- in this case we are using 4-bit inputs tree_depth = 2 # depth of the tree # Build x = pyrtl.Input(bitwidth=1, name='x') y = pyrtl.Input(bitwidth=1, name='y') out_bin = pyrtl.Output(bitwidth=tree_depth, name='out_bin') valid_out = pyrtl.Output(bitwidth=1, name='valid_out') attributes = pyrtl.concat_list([x, y]) # list of the tree's attributes tree_nodes = [[2, 0], [1, 0], [1, 1]] # [threshold, attribute_index] RT = FlatRaceTree(inp_res, tree_depth, attributes, tree_nodes) tree_out = RT.tree() out_bin <<= tree_out[0] valid_out <<= tree_out[1] # Simulate xin, yin = race_testval(2), race_testval(3) sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace)
def test_log_op_output(self): o = pyrtl.Output(1) w = pyrtl.WireVector(1) with self.assertRaises(pyrtl.PyrtlInternalError): x = w & o
and will be demonstrated here. """ import pyrtl from pyrtl.analysis import estimate # --- Part 1: Timing Analysis ------------------------------------------------ # Timing and area usage are key considerations of any hardware block that one # makes. PyRTL provides functions to do these operations. # Creating a sample hardware block pyrtl.reset_working_block() const_wire = pyrtl.Const(6, bitwidth=4) in_wire2 = pyrtl.Input(bitwidth=4, name="input2") out_wire = pyrtl.Output(bitwidth=5, name="output") out_wire <<= const_wire + in_wire2 # Now we will do the timing analysis as well as print out the critical path # Generating timing analysis information print("Pre Synthesis:") timing = estimate.TimingAnalysis() timing.print_max_length() # We are also able to print out the critical paths as well as get them # back as an array. critical_path_info = timing.critical_path() # --- Part 2: Area Analysis --------------------------------------------------
def test_slice_output(self): o = pyrtl.Output(2) with self.assertRaises(pyrtl.PyrtlInternalError): x = o[0]
def test_super_stress_test(self): allin = [pyrtl.Input(1, n) for n in 'abxyijkmzcd'] a, b, x, y, i, j, k, m, z, c, d = allin allout = [pyrtl.Output(4, 'var' + str(index)) for index in range(5)] var0, var1, var2, var3, var4 = allout """ 1 with a: # a 2 with b: # not(a) and b 3 with x: # not(a) and b and x 4 with otherwise: # not(a) and b and not(x) 5 with y: # not(a) and b and y; check(3,4) 6 with i: # not(a) and b and y and i; check(3,4) 7 with j: # not(a) and b and y and not(i) and j; check(3,4) 8 with otherwise: # not(a) and b and y and not(i) and not(j): check(3,4) 9 with k: # not(a) and b and y and k; check(3,4,6,7,8) 8 with otherwise: # not(a) and b and y and not(k): check(?) 10 with m: # not(a) and b and y and m; check(?) 11 with otherwise: #not(a) and not(b) 12 with z: #not(a) and not(b) and z 13 with c: #c; check(1,2,3,4,5,6,7,8,9,10,11,12) 14 with d: #not(c) and d; check(1,2,3,4,5,6,7,8,9,10,11,12) """ with pyrtl.conditional_assignment: with a: var0 |= 1 with b: with x: var0 |= 2 with pyrtl.otherwise: var0 |= 3 with y: with i: var1 |= 1 with j: var1 |= 2 with pyrtl.otherwise: var1 |= 3 with k: var2 |= 1 with pyrtl.otherwise: var2 |= 2 with m: var3 |= 3 with pyrtl.otherwise: with z: var0 |= 4 with c: var4 |= 1 with d: var4 |= 2 sim_trace = pyrtl.SimulationTrace() sim = pyrtl.FastSimulation(tracer=sim_trace) for cycle in range(2**len(allin)): inputs = {v: 0x1 & (cycle >> i) for i, v in enumerate(allin[::-1])} sim.step(inputs) for cycle in range(len(sim_trace)): t0, t1, t2, t3, t4 = 0, 0, 0, 0, 0 def v(var): return sim_trace.trace[var][cycle] if v(a): t0 = 1 elif v(b): if v(x): t0 = 2 else: t0 = 3 if v(y): if v(i): t1 = 1 elif v(j): t1 = 2 else: t1 = 3 if v(k): t2 = 1 else: t2 = 2 if v(m): t3 = 3 else: if v(z): t0 = 4 if v(c): t4 = 1 elif v(d): t4 = 2 self.assertEqual(v(var0), t0) self.assertEqual(v(var1), t1) self.assertEqual(v(var2), t2) self.assertEqual(v(var3), t3) self.assertEqual(v(var4), t4)
def setUp(self): pyrtl.reset_working_block() self.in1 = pyrtl.Input(8, "in1") self.in2 = pyrtl.Input(8, "in2") self.out = pyrtl.Output(16, "out")
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 A = pyrtl.Input(bitwidth=32, name='A') B = pyrtl.Input(bitwidth=32, name='B') result = pyrtl.Output(bitwidth=32, name='result') result <<= fib(A, B) sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for cycle in range(16): sim.step({'A': 1, 'B': 2}) sim_trace.render_trace()
a, b = pyrtl.match_bitwidth(a, b) # this function is a function that allows us to match the bitwidth of multiple # different wires. By default, it zero extends the shorter bits if len(a) == 1: sumbits, carry_out = one_bit_add(a, b, carry_in) else: lsbit, ripplecarry = one_bit_add(a[0], b[0], carry_in) msbits, carry_out = ripple_add(a[1:], b[1:], ripplecarry) sumbits = pyrtl.concat(msbits, lsbit) return sumbits, carry_out # Inputs and outputs of the module load = pyrtl.Input(1, 'load') data = pyrtl.Input(8, 'data') out = pyrtl.Output(8, 'out') # Simple logic to allow loading of the counter counter = pyrtl.Register(8, 'counter') sum, carry_out = ripple_add(counter, pyrtl.Const("1'b1")) counter.next <<= pyrtl.mux(load, sum, data) out <<= counter # Setup the simulation sim_trace = pyrtl.SimulationTrace([load, data, out]) sim = pyrtl.Simulation(tracer=sim_trace) # Run until receive a 'q' from the named pipe while True: cmd = os.read(inFifo, 3) if cmd != '': if cmd == 'q':
def test_adder(self): inwire1, inwire2 = pyrtl.Input(bitwidth=3), pyrtl.Input(bitwidth=3) outwire = pyrtl.Output(bitwidth=4) outwire <<= inwire1 + inwire2 self.everything_t_procedure()
b = pyrtl.Input(bitwidth=1, name='b') c = pyrtl.Input(bitwidth=1, name='c') d = pyrtl.Input(bitwidth=1, name='d') e = pyrtl.Input(bitwidth=1, name='e') # Declare control inputs # < add your code here > s = pyrtl.Input(bitwidth=3, name='s') #s1 = pyrtl.Input(bitwidth=1, name='s1') #s2 = pyrtl.Input(bitwidth=1, name='s2') # Declare outputs # < add your code here > o = pyrtl.Output(bitwidth=1, name='o') # Describe your 5:1 MUX implementation # < add your code here > sel0 = s[2] sel1 = s[1] sel2 = s[0] temp1 = ((~sel0) & (~sel1) & (~sel2)) & a temp2 = ((~sel0) & (~sel1) & (sel2)) & b temp3 = ((~sel0) & (sel1) & (~sel2)) & c temp4 = ((~sel0) & (sel1) & (sel2)) & d temp5 = ((sel0) & (~sel1) & (~sel2)) & e o <<= temp1 | temp2 | temp3 | temp4 | temp5
def test_assign_output(self): o = pyrtl.Output(1) w = pyrtl.WireVector(1) with self.assertRaises(pyrtl.PyrtlInternalError): w <<= o
""" Example 3: A State Machine built with ConditionalUpdate In this example we describe how ConditionalUpdate works in the context of a vending machine that will dispense an item when it has received 4 tokens. If a refund is requested, it returns the tokens. """ import pyrtl token_in = pyrtl.Input(1, 'token_in') req_refund = pyrtl.Input(1, 'req_refund') dispense = pyrtl.Output(1, 'dispense') refund = pyrtl.Output(1, 'refund') state = pyrtl.Register(3, 'state') # First new step, let's enumerate a set of constant to serve as our states WAIT, TOK1, TOK2, TOK3, DISPENSE, REFUND = [ pyrtl.Const(x, bitwidth=3) for x in range(6) ] # Now we could build a state machine using just the registers and logic discussed # in the earlier examples, but doing operations *conditional* on some input is a pretty # fundamental operation in hardware design. PyRTL provides a class "ConditionalUpdate" # to provide a predicated update to a registers, wires, and memories. # # Conditional assignments are specified with a "|=" instead of a "<<=" operator. The # conditional assignment is only value in the context of a condition, and update to those # values only happens when that condition is true. In hardware this is implemented # with a simple mux -- for people coming from software it is important to remember that this # is describing a big logic function NOT an "if-then-else" clause. All of these things will # execute straight through when "build_everything" is called. More comments after the code.