From faf8dd792bb8f9fde465cb961c9954e9dfe47654 Mon Sep 17 00:00:00 2001 From: Akash Dhiraj Date: Tue, 18 Jun 2024 17:31:02 -0400 Subject: [PATCH] Queues all working! --- .../correctness/queues/binheap/binheap.py | 173 +++++++++++------- .../test/correctness/queues/binheap/fifo.py | 20 +- .../test/correctness/queues/binheap/pifo.py | 147 ++++++--------- .../queues/binheap/stable_binheap.py | 15 +- 4 files changed, 190 insertions(+), 165 deletions(-) diff --git a/calyx-py/test/correctness/queues/binheap/binheap.py b/calyx-py/test/correctness/queues/binheap/binheap.py index 4603e00c52..34f0f08b4a 100644 --- a/calyx-py/test/correctness/queues/binheap/binheap.py +++ b/calyx-py/test/correctness/queues/binheap/binheap.py @@ -3,11 +3,11 @@ from calyx.tuple import insert_tuplify, insert_untuplify -def insert_swap(prog, name, width, len, idx_w): +def insert_swap(prog, name, width, size, idx_w): """Inserts the component `swap` into the program. It takes two `idx_w`-bit inputs `a` and `b` and accepts a memory by reference. - The memory is a `len`-element memory of `width`-bit elements. + The memory is a `size`-element memory of `width`-bit elements. It swaps the values in the memory at addresses `a` and `b`. """ @@ -15,7 +15,7 @@ def insert_swap(prog, name, width, len, idx_w): a = comp.input("a", idx_w) b = comp.input("b", idx_w) - mem = comp.seq_mem_d1("mem", width, len, idx_w, is_ref=True) + mem = comp.seq_mem_d1("mem", width, size, idx_w, is_ref=True) val_a = comp.reg(width) val_b = comp.reg(width) @@ -31,20 +31,21 @@ def insert_swap(prog, name, width, len, idx_w): return comp -def insert_binheap(prog, name, queue_len_factor): +def insert_binheap(prog, name, queue_size_factor): """Inserts the component `binheap` into the program. It is a minimum binary heap, represented as an array. It has: - three inputs, `cmd`, `value`, and `rank`. - - one memory, `mem`, of size `(2**queue_len_factor)`. + - one memory, `mem`, of size `2**queue_size_factor`. - two ref registers, `ans` and `err`. """ comp = prog.component(name) - max_queue_len = 2**queue_len_factor + max_queue_size = 2**queue_size_factor + addr_size = queue_size_factor cmd = comp.input("cmd", 2) # If it is 0, we pop. @@ -55,13 +56,11 @@ def insert_binheap(prog, name, queue_len_factor): rank = comp.input("rank", 64) value = comp.input("value", 32) - swap = comp.cell( - "swap", insert_swap(prog, "swap", 96, max_queue_len, queue_len_factor) - ) + swap = comp.cell("swap", insert_swap(prog, "swap", 96, max_queue_size, addr_size)) tuplify = comp.cell("tuplify", insert_tuplify(prog, "tuplify", 64, 32)) untuplify = comp.cell("untuplify", insert_untuplify(prog, "untuplify", 64, 32)) - mem = comp.seq_mem_d1("mem", 96, max_queue_len, queue_len_factor) + mem = comp.seq_mem_d1("mem", 96, max_queue_size, addr_size) # The memory to store the heap, represented as an array. # Each cell of the memory is 96 bits wide: a 64-bit rank and a 32-bit value. @@ -71,7 +70,7 @@ def insert_binheap(prog, name, queue_len_factor): err = comp.reg(1, "err", is_ref=True) # We'll raise this as a general error flag for overflow and underflow. - size = comp.reg(queue_len_factor) # The active length of the heap. + size = comp.reg(addr_size) # The active size of the heap. # Cells and groups to check which command we got. cmd_eq_0 = comp.eq_use(cmd, 0) @@ -80,18 +79,20 @@ def insert_binheap(prog, name, queue_len_factor): # Cells and groups to check for overflow and underflow. size_eq_0 = comp.eq_use(size.out, 0) - size_eq_max = comp.eq_use(size.out, max_queue_len - 1) + full = comp.reg(1) + set_full_on = comp.reg_store(full, 1, "set_full_on") + set_full_off = comp.reg_store(full, 0, "set_full_off") - current_idx = comp.reg(queue_len_factor) + current_idx = comp.reg(addr_size) current_rank = comp.reg(64) - parent_idx = comp.reg(queue_len_factor) + parent_idx = comp.reg(addr_size) parent_rank = comp.reg(64) - child_l_idx = comp.reg(queue_len_factor) + child_l_idx = comp.reg(addr_size) child_l_rank = comp.reg(64) - child_r_idx = comp.reg(queue_len_factor) + child_r_idx = comp.reg(addr_size) child_r_rank = comp.reg(64) # current_idx := 0 @@ -115,29 +116,29 @@ def insert_binheap(prog, name, queue_len_factor): # err := 0 lower_err = comp.reg_store(err, 0, "lower_err") - sub = comp.sub(queue_len_factor) - rsh = comp.rsh(queue_len_factor) + sub = comp.sub(addr_size) + rsh = comp.rsh(addr_size) with comp.group("find_parent_idx") as find_parent_idx: # Find the parent of the `current_idx`th element and store it in `parent_idx`. # parent_idx := floor((current_idx − 1) / 2) sub.left = current_idx.out sub.right = 1 rsh.left = sub.out - rsh.right = cb.const(queue_len_factor, 1) + rsh.right = cb.const(addr_size, 1) parent_idx.in_ = rsh.out parent_idx.write_en = cb.HI find_parent_idx.done = parent_idx.done - add_1 = comp.add(queue_len_factor) - add_2 = comp.add(queue_len_factor) - lsh = comp.lsh(queue_len_factor) + add_1 = comp.add(addr_size) + add_2 = comp.add(addr_size) + lsh = comp.lsh(addr_size) with comp.group("find_child_idx") as find_child_idx: # Find the children of `current_idx`th element and store # them in child_l_idx and child_r_idx. # child_l_idx := (2 * current_idx) + 1 # child_r_idx := (2 * current_idx) + 2 lsh.left = current_idx.out - lsh.right = cb.const(queue_len_factor, 1) + lsh.right = cb.const(addr_size, 1) add_1.left = 1 add_1.right = lsh.out child_l_idx.write_en = cb.HI @@ -203,56 +204,90 @@ def extract_snd(name, idx, out): child_r_rank, ) - # current_rank < parent_rank - current_lt_parent = comp.lt_use(current_rank.out, parent_rank.out) - - le_1 = comp.le(queue_len_factor) - le_2 = comp.le(64) + lt_1 = comp.lt(64) + lt_2 = comp.lt(addr_size) + while_and = comp.and_(1) + with comp.comb_group("current_lt_parent") as current_lt_parent: + # Check if the `current_idx`th element should be swapped with its parent. + # current_rank < parent_rank AND parent_idx < current_idx + lt_1.left = current_rank.out + lt_1.right = parent_rank.out + + lt_2.left = parent_idx.out + lt_2.right = current_idx.out + + while_and.left = lt_1.out + while_and.right = lt_2.out + + le_1 = comp.le(addr_size) + le_2 = comp.le(addr_size) + le_3 = comp.le(64) + inner_or = comp.or_(1) if_or = comp.or_(1) with comp.comb_group("child_l_swap") as child_l_swap: # Check if the `current_idx`th element should be swapped with its left child. - # size <= child_r_idx OR child_l_rank <= child_r_rank + # size <= child_r_idx OR child_r_idx <= current_idx OR child_l_rank <= child_r_rank le_1.left = size.out le_1.right = child_r_idx.out - le_2.left = child_l_rank.out - le_2.right = child_r_rank.out + le_2.left = child_r_idx.out + le_2.right = current_idx.out + + le_3.left = child_l_rank.out + le_3.right = child_r_rank.out - if_or.left = le_1.out - if_or.right = le_2.out + inner_or.left = le_1.out + inner_or.right = le_2.out - lt_1 = comp.lt(queue_len_factor) - lt_2 = comp.lt(64) - lt_3 = comp.lt(queue_len_factor) - lt_4 = comp.lt(64) - and_1 = comp.and_(1) - and_2 = comp.and_(1) + if_or.left = inner_or.out + if_or.right = le_3.out + + lt_l_1 = comp.lt(addr_size) + lt_l_2 = comp.lt(addr_size) + lt_l_3 = comp.lt(64) + lt_r_1 = comp.lt(addr_size) + lt_r_2 = comp.lt(addr_size) + lt_r_3 = comp.lt(64) + and_l_1 = comp.and_(1) + and_l_2 = comp.and_(1) + and_r_1 = comp.and_(1) + and_r_2 = comp.and_(1) while_or = comp.or_(1) with comp.comb_group("current_gt_children") as current_gt_children: - # Check if the `current_idx`th element should be swapped with its left OR right. - # child_l_idx < size AND child_l_rank < current_rank + # Check if the `current_idx`th element should be swapped with its left OR right child. + # child_l_idx < size AND current_idx < child_l_idx AND child_l_rank < current_rank # OR - # child_r_idx < size AND child_r_rank < current_rank - lt_1.left = child_l_idx.out - lt_1.right = size.out + # child_r_idx < size AND current_idx < child_r_idx AND child_r_rank < current_rank + lt_l_1.left = child_l_idx.out + lt_l_1.right = size.out + + lt_l_2.left = current_idx.out + lt_l_2.right = child_l_idx.out - lt_2.left = child_l_rank.out - lt_2.right = current_rank.out + lt_l_3.left = child_l_rank.out + lt_l_3.right = current_rank.out - lt_3.left = child_r_idx.out - lt_3.right = size.out + lt_r_1.left = child_r_idx.out + lt_r_1.right = size.out - lt_4.left = child_r_rank.out - lt_4.right = current_rank.out + lt_r_2.left = current_idx.out + lt_r_2.right = child_r_idx.out - and_1.left = lt_1.out - and_1.right = lt_2.out + lt_r_3.left = child_r_rank.out + lt_r_3.right = current_rank.out - and_2.left = lt_3.out - and_2.right = lt_4.out + and_l_1.left = lt_l_1.out + and_l_1.right = lt_l_2.out + and_l_2.left = and_l_1.out + and_l_2.right = lt_l_3.out - while_or.left = and_1.out - while_or.right = and_2.out + and_r_1.left = lt_r_1.out + and_r_1.right = lt_r_2.out + and_r_2.left = and_r_1.out + and_r_2.right = lt_r_3.out + + while_or.left = and_l_2.out + while_or.right = and_r_2.out peek = extract_snd("peek", 0, ans) @@ -261,6 +296,7 @@ def extract_snd(name, idx, out): comp.decr(size), set_idx_zero, cb.invoke(swap, in_a=current_idx.out, in_b=size.out, ref_mem=mem), + comp.mem_store_d1(mem, size.out, cb.const(96, 0), "zero_leaf"), extract_current_rank, find_child_idx, extract_child_l_rank, @@ -306,7 +342,7 @@ def extract_snd(name, idx, out): extract_current_rank, # Bubble Up cb.while_with( - current_lt_parent, + cb.CellAndGroup(while_and, current_lt_parent), [ cb.invoke(swap, in_a=parent_idx.out, in_b=current_idx.out, ref_mem=mem), set_idx_parent, @@ -320,12 +356,23 @@ def extract_snd(name, idx, out): lower_err, cb.if_with( cmd_eq_0, - cb.if_with(size_eq_0, raise_err, pop), + cb.if_(full.out, + [pop, set_full_off], + cb.if_with(size_eq_0, raise_err, pop) + ), cb.if_with( cmd_eq_1, - cb.if_with(size_eq_0, raise_err, peek), + cb.if_(full.out, + peek, + cb.if_with(size_eq_0, raise_err, peek) + ), cb.if_with( - cmd_eq_2, cb.if_with(size_eq_max, raise_err, push), raise_err + cmd_eq_2, + [ + cb.if_(full.out, raise_err, push), + cb.if_with(size_eq_0, set_full_on) + ], + raise_err ), ), ), @@ -362,12 +409,12 @@ def insert_main(prog): comp = prog.component("main") - queue_len_factor = 4 + queue_size_factor = 4 - binheap = insert_binheap(prog, "binheap", queue_len_factor) + binheap = insert_binheap(prog, "binheap", queue_size_factor) binheap = comp.cell("binheap", binheap) - out = comp.seq_mem_d1("out", 32, 15, queue_len_factor, is_external=True) + out = comp.seq_mem_d1("out", 32, 15, queue_size_factor, is_external=True) ans = comp.reg(32) err = comp.reg(1) diff --git a/calyx-py/test/correctness/queues/binheap/fifo.py b/calyx-py/test/correctness/queues/binheap/fifo.py index f5c021b9d8..259a607629 100644 --- a/calyx-py/test/correctness/queues/binheap/fifo.py +++ b/calyx-py/test/correctness/queues/binheap/fifo.py @@ -5,20 +5,19 @@ from stable_binheap import insert_stable_binheap -def insert_binheap_fifo(prog, name, factor): +def insert_binheap_fifo(prog, name, queue_size_factor): """Inserts the component `fifo` into the program. - It is implemented via a binary heap + It is a first in, first out queue implemented via binary heap It has: - two inputs, `cmd` and `value`. - - one memory, `mem`, of size 10. - - two registers, `next_write` and `next_read`. + - one memory, `mem`, of size `2**queue_size_factor`. - two ref registers, `ans` and `err`. """ comp = prog.component(name) - binheap = insert_stable_binheap(prog, "binheap", factor) + binheap = insert_stable_binheap(prog, "binheap", queue_size_factor) binheap = comp.cell("binheap", binheap) cmd = comp.input("cmd", 2) @@ -28,7 +27,14 @@ def insert_binheap_fifo(prog, name, factor): err = comp.reg(1, "err", is_ref=True) comp.control += [ - cb.invoke(binheap, in_value=value, in_rank=cb.const(32, 1), in_cmd=cmd, ref_ans=ans, ref_err=err), + cb.invoke( + binheap, + in_value=value, + in_rank=cb.const(32, 1), + in_cmd=cmd, + ref_ans=ans, + ref_err=err, + ) ] return comp @@ -38,7 +44,7 @@ def build(): """Top-level function to build the program.""" num_cmds = int(sys.argv[1]) prog = cb.Builder() - fifo = insert_binheap_fifo(prog, "fifo", 6) + fifo = insert_binheap_fifo(prog, "fifo", 4) qc.insert_main(prog, fifo, num_cmds) return prog.program diff --git a/calyx-py/test/correctness/queues/binheap/pifo.py b/calyx-py/test/correctness/queues/binheap/pifo.py index 2f7db0ec55..984eb1fa31 100644 --- a/calyx-py/test/correctness/queues/binheap/pifo.py +++ b/calyx-py/test/correctness/queues/binheap/pifo.py @@ -7,14 +7,15 @@ def insert_flow_inference(comp, value, flow, boundary, group): """The flow is needed when the command is a push. - If the value to be pushed is less than or equal to {boundary}, + If the value to be pushed is less than or equal to `boundary`, the value belongs to flow 0. Otherwise, the value belongs to flow 1. - This method adds a group to the component {comp} that does this. - 1. Within component {comp}, creates a group called {group}. - 2. Within {group}, creates a cell {cell} that checks for less-than. - 3. Puts the values {boundary} and {value} into the left and right ports of {cell}. - 4. Then puts the answer of the computation into {flow}. + + This method adds a group to the component `comp` that does this. + 1. Within component `comp`, creates a group called `group`. + 2. Within `group`, creates a cell `cell` that checks for less-than. + 3. Puts the values `boundary` and `value` into the left and right ports of `cell`. + 4. Then puts the answer of the computation into `flow`. 5. Returns the group that does this. """ cell = comp.lt(32) @@ -28,6 +29,15 @@ def insert_flow_inference(comp, value, flow, boundary, group): def insert_binheap_pifo(prog, name, boundary, factor): + """Inserts the component `pifo` into the program. + + It is a two-flow, round-robin queue implemented via binary heap + + It has: + - two inputs, `cmd` and `value`. + - one memory, `mem`, of size `2**queue_size_factor`. + - two ref registers, `ans` and `err`. + """ comp = prog.component(name) binheap = insert_stable_binheap(prog, "binheap", factor) @@ -43,10 +53,14 @@ def insert_binheap_pifo(prog, name, boundary, factor): cmd_eq_2 = comp.eq_use(cmd, 2) flow_in = comp.reg(1, "flow_in") - infer_flow_in = insert_flow_inference(comp, value, flow_in, boundary, "infer_flow_in") + infer_flow_in = insert_flow_inference( + comp, value, flow_in, boundary, "infer_flow_in" + ) flow_out = comp.reg(1, "flow_out") - infer_flow_out = insert_flow_inference(comp, ans.out, flow_out, boundary, "infer_flow_out") + infer_flow_out = insert_flow_inference( + comp, ans.out, flow_out, boundary, "infer_flow_out" + ) r_a = comp.reg(32, "r_a") r_a_incr_2 = comp.incr(r_a, 2) @@ -63,104 +77,53 @@ def insert_binheap_pifo(prog, name, boundary, factor): comp.control += [ cb.if_with(init_eq_0, [comp.reg_store(r_b, 1), comp.incr(init)]), infer_flow_in, - cb.if_(flow_in.out, - cb.invoke(binheap, - in_value=value, in_rank=r_b.out, in_cmd=cmd, - ref_ans=ans, ref_err=err), - cb.invoke(binheap, - in_value=value, in_rank=r_a.out, in_cmd=cmd, - ref_ans=ans, ref_err=err) + cb.if_( + flow_in.out, + cb.invoke( + binheap, + in_value=value, + in_rank=r_b.out, + in_cmd=cmd, + ref_ans=ans, + ref_err=err, + ), + cb.invoke( + binheap, + in_value=value, + in_rank=r_a.out, + in_cmd=cmd, + ref_ans=ans, + ref_err=err, + ), ), infer_flow_out, - cb.if_with(cmd_eq_0, # pop - cb.if_with(turn_neq_flow_out, - cb.if_(flow_out.out, - r_a_incr_2, - r_b_incr_2), - comp.incr(turn) - ) + cb.if_with( + cmd_eq_0, + cb.if_with( + turn_neq_flow_out, + cb.if_(flow_out.out, r_a_incr_2, r_b_incr_2), + comp.incr(turn), + ) ), - cb.if_with(cmd_eq_2, # push - cb.if_(flow_in.out, - r_b_incr_2, - r_a_incr_2) + cb.if_with( + cmd_eq_2, + cb.if_( + flow_in.out, + r_b_incr_2, + r_a_incr_2 + ) ) ] return comp -def insert_main(prog): - comp = prog.component("main") - - factor = 4 - - pifo = insert_binheap_pifo(prog, "binheap_pifo", 200, factor) - pifo = comp.cell("binheap_pifo", pifo) - - out = comp.comb_mem_d1("out", 32, 15, factor, is_external=True) - - ans = comp.reg(32) - err = comp.reg(1) - - err_incr = comp.incr(err) - - index = 0 - - def push(value): - return cb.invoke(pifo, - in_value=cb.const(32, value), in_cmd=cb.const(2, 2), - ref_ans=ans, ref_err=err) - - def pop_and_store(): - nonlocal index - index += 1 - - - - return [ - cb.invoke(pifo, - in_value=cb.const(32, 50), in_cmd=cb.const(2,0), - ref_ans=ans, ref_err=err), - err_incr, - cb.if_(err.out, - comp.mem_store_d1(out, index - 1, ans.out, f"store_ans_{index}") - ) - ] - - def peak_and_store(): - nonlocal index - index += 1 - - return [ - cb.invoke(pifo, - in_value=cb.const(32, 50), in_cmd=cb.const(2,1), - ref_ans=ans, ref_err=err), - comp.mem_store_d1(out, index - 1, ans.out, f"store_ans_{index}") - ] - - comp.control += [ - push(242), - pop_and_store(), - push(139), - pop_and_store(), - push(155), - push(18), - push(87), - push(389), - pop_and_store(), - pop_and_store(), - pop_and_store(), - pop_and_store() - ] - def build(): """Top-level function to build the program.""" num_cmds = int(sys.argv[1]) prog = cb.Builder() - pifo = insert_binheap_pifo(prog, "pifo", 200, 6) + pifo = insert_binheap_pifo(prog, "pifo", 200, 4) qc.insert_main(prog, pifo, num_cmds) - #insert_main(prog) return prog.program diff --git a/calyx-py/test/correctness/queues/binheap/stable_binheap.py b/calyx-py/test/correctness/queues/binheap/stable_binheap.py index 8b26d75019..96e486a6a7 100644 --- a/calyx-py/test/correctness/queues/binheap/stable_binheap.py +++ b/calyx-py/test/correctness/queues/binheap/stable_binheap.py @@ -27,7 +27,7 @@ def insert_stable_binheap(prog, name, queue_len_factor): err = comp.reg(1, "err", is_ref=True) i = comp.reg(32) - + cat = comp.cat(32, 32) with comp.continuous: @@ -35,7 +35,14 @@ def insert_stable_binheap(prog, name, queue_len_factor): cat.right = i.out comp.control += [ - cb.invoke(below, in_value=value, in_rank=cat.out, in_cmd=cmd, ref_ans=ans, ref_err=err), + cb.invoke( + below, + in_value=value, + in_rank=cat.out, + in_cmd=cmd, + ref_ans=ans, + ref_err=err + ), comp.incr(i) ] @@ -129,6 +136,7 @@ def peek_and_store(): ] comp.control += [ + # works like the usual heap? push(9, 9), push(12, 12), push(6, 6), @@ -143,6 +151,7 @@ def peek_and_store(): pop_and_store(), pop_and_store(), pop_and_store(), + # breaks ties correctly? push(3, 2), push(4, 2), push(5, 2), @@ -152,7 +161,7 @@ def peek_and_store(): pop_and_store(), pop_and_store(), pop_and_store(), - pop_and_store() + pop_and_store(), ]