def gen_reduce_impl(stmt, arr_size, s_idx): """ Returns a dictionary containing Calyx cells, wires and control needed to implement a map statement. Similar to gen_map_impl, with an implementation of a body of the `reduce` statement instead of an implementation of a `map` statement. """ stdlib = Stdlib() op_name = "mult" if stmt.op.body.op == "mul" else "add" cells = [ Cell(CompVar(f"le{s_idx}"), stdlib.op("lt", 32, signed=False)), Cell(CompVar(f"idx{s_idx}"), stdlib.register(32)), Cell(CompVar(f"adder_idx{s_idx}"), stdlib.op("add", 32, signed=False)), Cell(CompVar(f"adder_op{s_idx}"), stdlib.op(f"{op_name}", 32, signed=False)), ] wires = [ emit_cond_group(s_idx, arr_size), emit_idx_group(s_idx), emit_eval_body_group(s_idx, stmt, 0), ] control = While( port=CompPort(CompVar(f"le{s_idx}"), "out"), cond=CompVar(f"cond{s_idx}"), body=SeqComp([Enable(f"eval_body{s_idx}"), Enable(f"incr_idx{s_idx}")]), ) return {"cells": cells, "wires": wires, "control": control}
def gen_map_impl(stmt, arr_size, bank_factor, s_idx): """ Returns a dictionary containing Calyx cells, wires and control needed to implement a map statement. (See gen_stmt_impl for format of the dictionary.) Generates these groups: - a group that implements the body of the map statement - a group that increments an index to access the map input array - a group that implements the loop condition, checking if the index has reached the end of the input array """ stdlib = Stdlib() cells = [] for b in range(bank_factor): cells.extend([ Cell(CompVar(f"le_b{b}_{s_idx}"), stdlib.op("lt", 32, signed=False)), Cell(CompVar(f"idx_b{b}_{s_idx}"), stdlib.register(32)), Cell( CompVar(f"adder_idx_b{b}_{s_idx}"), stdlib.op("add", 32, signed=False), ), ]) op_name = "mult" if stmt.op.body.op == "mul" else "add" for b in range(bank_factor): cells.append( Cell( CompVar(f"adder_op_b{b}_{s_idx}"), stdlib.op(f"{op_name}", 32, signed=False), )) wires = [] for b in range(bank_factor): wires.extend([ emit_cond_group(s_idx, arr_size // bank_factor, b), emit_idx_group(s_idx, b), emit_eval_body_group(s_idx, stmt, b), ]) map_loops = [] for b in range(bank_factor): b_suffix = f"_b{str(b)}_" map_loops.append( While( CompPort(CompVar(f"le{b_suffix}{s_idx}"), "out"), CompVar(f"cond{b_suffix}{s_idx}"), SeqComp([ Enable(f"eval_body{b_suffix}{s_idx}"), Enable(f"incr_idx{b_suffix}{s_idx}"), ]), )) control = ParComp(map_loops) return {"cells": cells, "wires": wires, "control": control}
def cells(): stdlib = Stdlib() memories = [ Cell(input, stdlib.mem_d1(input_bitwidth, n, bitwidth), is_external=True), Cell(phis, stdlib.mem_d1(input_bitwidth, n, bitwidth), is_external=True), ] r_regs = [ Cell(CompVar(f"r{r}"), stdlib.register(input_bitwidth)) for r in range(n) ] A_regs = [ Cell(CompVar(f"A{r}"), stdlib.register(input_bitwidth)) for r in range(n) ] mul_regs = [ Cell(CompVar(f"mul{i}"), stdlib.register(input_bitwidth)) for i in range(n // 2) ] phi_regs = [ Cell(CompVar(f"phi{r}"), stdlib.register(input_bitwidth)) for r in range(n) ] mod_pipes = [ Cell( CompVar(f"mod_pipe{r}"), stdlib.op("div_pipe", input_bitwidth, signed=True), ) for r in range(n) ] mult_pipes = [ Cell( CompVar(f"mult_pipe{i}"), stdlib.op("mult_pipe", input_bitwidth, signed=True), ) for i in range(n // 2) ] adds = [ Cell(CompVar(f"add{i}"), stdlib.op("add", input_bitwidth, signed=True)) for i in range(n // 2) ] subs = [ Cell(CompVar(f"sub{i}"), stdlib.op("sub", input_bitwidth, signed=True)) for i in range(n // 2) ] return ( memories + r_regs + A_regs + mul_regs + phi_regs + mod_pipes + mult_pipes + adds + subs )
def emit_mem_decl(name, size, par): """ Returns N memory declarations, where N = `par`. """ stdlib = Stdlib() banked_mems = [] for i in range(par): banked_mems.append( Cell( CompVar(f"{name}_b{i}"), stdlib.mem_d1(32, size // par, 32), is_external=True, )) return banked_mems
def emit(prog): """ Returns a string containing a Calyx program, compiled from `prog`, a MrXL program. """ cells, wires, control = [], [], [] # All arrays must be the same size. The first array we see determines the # size that we'll assume for the rest of the program's arrays. arr_size = None # Collect banking factors. name2par = dict() for stmt in prog.stmts: if isinstance(stmt.op, ast.Map): name2par[stmt.dest] = stmt.op.par for b in stmt.op.bind: name2par[b.src] = stmt.op.par # Collect memory and register declarations. used_names = [] stdlib = Stdlib() for decl in prog.decls: used_names.append(decl.name) if decl.type.size: # A memory arr_size = decl.type.size cells.extend( emit_mem_decl(decl.name, decl.type.size, name2par[decl.name])) else: # A register cells.append(Cell(CompVar(decl.name), stdlib.register(32))) # Collect implicit memory and register declarations. for stmt in prog.stmts: if stmt.dest not in used_names: if isinstance(stmt.op, ast.Map): cells.extend( emit_mem_decl(stmt.dest, arr_size, name2par[stmt.dest])) else: raise NotImplementedError("Generating register declarations") # cells.append(emit_reg_decl(stmt.dest, 32)) used_names.append(stmt.dest) # Generate Calyx. for i, stmt in enumerate(prog.stmts): stmt_impl = gen_stmt_impl(stmt, arr_size, name2par, i) cells.extend(stmt_impl["cells"]) wires.extend(stmt_impl["wires"]) control.append(stmt_impl["control"]) program = Program( imports=[ Import("primitives/core.futil"), Import("primitives/binary_operators.futil"), ], components=[ Component( name="main", inputs=[], outputs=[], structs=cells + wires, controls=SeqComp(control), ) ], ) program.emit()
def generate_cells(degree: int, width: int, int_width: int, is_signed: bool) -> List[Cell]: stdlib = Stdlib() frac_width = width - int_width init_cells = [ Cell(CompVar("exponent_value"), stdlib.register(width)), Cell(CompVar("int_x"), stdlib.register(width)), Cell(CompVar("frac_x"), stdlib.register(width)), Cell(CompVar("m"), stdlib.register(width)), Cell(CompVar("and0"), stdlib.op("and", width, signed=False)), Cell(CompVar("and1"), stdlib.op("and", width, signed=False)), Cell(CompVar("rsh"), stdlib.op("rsh", width, signed=False)), ] + ([Cell(CompVar("lt"), stdlib.op("lt", width, signed=is_signed))] if is_signed else []) pow_registers = [ Cell(CompVar(f"p{i}"), stdlib.register(width)) for i in range(2, degree + 1) ] product_registers = [ Cell(CompVar(f"product{i}"), stdlib.register(width)) for i in range(2, degree + 1) ] sum_registers = [ Cell(CompVar(f"sum{i}"), stdlib.register(width)) for i in range(1, (degree // 2) + 1) ] adds = [ Cell( CompVar(f"add{i}"), stdlib.fixed_point_op("add", width, int_width, frac_width, signed=is_signed), ) for i in range(1, (degree // 2) + 1) ] pipes = [ Cell( CompVar(f"mult_pipe{i}"), stdlib.fixed_point_op("mult_pipe", width, int_width, frac_width, signed=is_signed), ) for i in range(1, degree + 1) ] # One extra `fp_pow` instance to compute e^{int_value}. pows = [ Cell(CompVar(f"pow{i}"), CompInst("fp_pow", [])) for i in range(1, degree + 1) ] reciprocal_factorials = [] for i in range(2, degree + 1): fixed_point_value = float_to_fixed_point(1.0 / factorial(i), frac_width) value = numeric_types.FixedPoint( str(fixed_point_value), width, int_width, is_signed=is_signed).unsigned_integer() reciprocal_factorials.append( Cell(CompVar(f"reciprocal_factorial{i}"), stdlib.constant(width, value))) # Constant values for the exponent to the fixed point `pow` function. constants = [ Cell(CompVar(f"c{i}"), stdlib.constant(width, i)) for i in range(2, degree + 1) ] + [ Cell( CompVar("one"), stdlib.constant( width, numeric_types.FixedPoint( "1.0", width, int_width, is_signed=is_signed).unsigned_integer(), ), ), Cell( CompVar("e"), stdlib.constant( width, numeric_types.FixedPoint( str(float_to_fixed_point(2.7182818284, frac_width)), width, int_width, is_signed=is_signed, ).unsigned_integer(), ), ), ] if is_signed: constants.append( Cell( CompVar("negative_one"), stdlib.constant( width, numeric_types.FixedPoint( "-1.0", width, int_width, is_signed=is_signed).unsigned_integer(), ), ), ) pipes.append( Cell( CompVar("div_pipe"), stdlib.fixed_point_op("div_pipe", width, int_width, frac_width, signed=is_signed), )) return (init_cells + constants + product_registers + pow_registers + sum_registers + adds + pipes + reciprocal_factorials + pows)
def generate_fp_pow_component(width: int, int_width: int, is_signed: bool) -> Component: """Generates a fixed point `pow` component, which computes the value x**y, where y must be an integer. """ stdlib = Stdlib() frac_width = width - int_width pow = CompVar("pow") count = CompVar("count") mul = CompVar("mul") lt = CompVar("lt") incr = CompVar("incr") cells = [ Cell(pow, stdlib.register(width)), Cell(count, stdlib.register(width)), Cell( mul, stdlib.fixed_point_op("mult_pipe", width, int_width, frac_width, signed=is_signed), ), Cell(lt, stdlib.op("lt", width, signed=is_signed)), Cell(incr, stdlib.op("add", width, signed=is_signed)), ] wires = [ Group( id=CompVar("init"), connections=[ Connect( ConstantPort( width, numeric_types.FixedPoint( "1.0", width, int_width, is_signed=is_signed).unsigned_integer(), ), CompPort(pow, "in"), ), Connect(ConstantPort(1, 1), CompPort(pow, "write_en")), Connect(ConstantPort(width, 0), CompPort(count, "in")), Connect(ConstantPort(1, 1), CompPort(count, "write_en")), Connect( ConstantPort(1, 1), HolePort(CompVar("init"), "done"), And( Atom(CompPort(pow, "done")), Atom(CompPort(count, "done")), ), ), ], ), Group( id=CompVar("execute_mul"), connections=[ Connect(ThisPort(CompVar("base")), CompPort(mul, "left")), Connect(CompPort(pow, "out"), CompPort(mul, "right")), Connect( ConstantPort(1, 1), CompPort(mul, "go"), Not(Atom(CompPort(mul, "done"))), ), Connect(CompPort(mul, "done"), CompPort(pow, "write_en")), Connect(CompPort(mul, "out"), CompPort(pow, "in")), Connect( CompPort(pow, "done"), HolePort(CompVar("execute_mul"), "done"), ), ], ), Group( id=CompVar("incr_count"), connections=[ Connect(ConstantPort(width, 1), CompPort(incr, "left")), Connect(CompPort(count, "out"), CompPort(incr, "right")), Connect(CompPort(incr, "out"), CompPort(count, "in")), Connect(ConstantPort(1, 1), CompPort(count, "write_en")), Connect( CompPort(count, "done"), HolePort(CompVar("incr_count"), "done"), ), ], ), CombGroup( id=CompVar("cond"), connections=[ Connect(CompPort(count, "out"), CompPort(lt, "left")), Connect(ThisPort(CompVar("integer_exp")), CompPort(lt, "right")), ], ), Connect(CompPort(CompVar("pow"), "out"), ThisPort(CompVar("out"))), ] return Component( "fp_pow", inputs=[ PortDef(CompVar("base"), width), PortDef(CompVar("integer_exp"), width), ], outputs=[PortDef(CompVar("out"), width)], structs=cells + wires, controls=SeqComp([ Enable("init"), While( CompPort(lt, "out"), CompVar("cond"), ParComp([Enable("execute_mul"), Enable("incr_count")]), ), ]), )
program = Program( imports=[ Import("primitives/core.futil"), Import("primitives/binary_operators.futil") ], components=generate_exp_taylor_series_approximation( degree, width, int_width, is_signed), ) # Append a `main` component for testing purposes. program.components.append( Component( "main", inputs=[], outputs=[], structs=[ Cell(CompVar("t"), Stdlib().register(width)), Cell(CompVar("x"), Stdlib().mem_d1(width, 1, 1), is_external=True), Cell( CompVar("ret"), Stdlib().mem_d1(width, 1, 1), is_external=True, ), Cell(CompVar("e"), CompInst("exp", [])), Group( id=CompVar("init"), connections=[ Connect( ConstantPort(1, 0), CompPort(CompVar("x"), "addr0"),