Small pyrope flow
This is a brainstorm of different options to specify alternative syntax.
Option 1:
pipe alu(in1, in2) -> (out_pipelined, out_live) {
let tmp = await[3] mul(in1, in2) forward in2
out_pipelined = await[1] add(tmp, in2)
out_live = await[1] add(tmp, input.in2)
}
Option 2:
// A 'pipe' is a basic tool that performs one job.
pipe mul(a, b) -> (c) { c = a * b }
pipe add(a, b) -> (c) { c = a + b }
pipe alu(in1, in2) -> (out_pipelined1, out_piepelined2, out_live) {
(tmp, in2_delayed) = await[3] (mul(in1, in2), in2)
out_pipelined1 = await[1] add(tmp, in2_delayed)
out_pipelined2 = await[1] add(tmp, await in2)
out_live = await[1] add(tmp, nowait in2)
// compile error if inputs come from different times without await/noawait
}
//--------
Option 1:
// A 'pipe' is a basic tool that performs one job.
pipe mul(a, b) -> (c) { c = a * b }
pipe add(a, b) -> (c) { c = a + b }
// A 'flow' is an assembly line that wires tools together.
flow alu(in1, in2) -> (out_pipelined, out_live) {
(tmp, in2_d) = await[3] (mul(in1, in2), in2)
out_pipelined = await[1] add(tmp, in2_d)
out_live = await[1] add(tmp, nowait in2)
}
flow accum_alu(in1,in2) -> (out) {
tmp = await[3] mul(in1, in2)
out = total // Output before add accumulation
total = await[1] add(nowait total, tmp) // accumulate over total
}
Option 2:
pipe mul(a, b) -> (c) { c = a * b }
pipe add(a, b) -> (c) { c = a + b }
flow alu(in1, in2) -> (out_pipelined, out_live) {
(tmp, in2_d) = wait[3] (mul(in1, in2), in2)
out_pipelined = wait[1] add(tmp, in2_d)
out_live = wait[1] add(tmp, wait[0] in2)
}
flow accum_alu(in1, in2) -> (out) {
reg total=0
let tmp = await[3] mul(in1, in2)
out = total
next total = await[1] add(wait[0] total, tmp) // Pick value from reg output, no pipeline
}
Option 3
pipe mul(a, b) -> (c) { c = a * b }
pipe add(a, b) -> (c) { c = a + b }
flow alu(in1, in2) -> (out_pipelined, out_live) {
let (tmp, in2_d) = delay[3] (mul(in1, in2), in2)
out_pipelined = delay[1] add(tmp, in2_d)
out_live = delay[1] add(tmp, delay[0] in2)
}
flow accum_alu(in1, in2) -> (out) {
reg total = 0
let prod = delay[3] mul(in1, in2) // prod @ t+3
let sum = add(total, prod) // ERROR total and prod different times
let sum_aligned = add(delay[0] total, prod) // prod @ 3, total current flop output
next total = delay[1] sum_aligned // total value not visible until next cycle
out = total // total is the current value (held) at any given cycle
Option 4:
// Primitives: Defined with 'pipe'. Their logic is a single expression.
pipe mul(a, b) -> (c) { c = a * b }
pipe add(a, b) -> (c) { c = a + b }
flow alu(in1, in2) -> (out_pipelined, out_live) {
let (tmp, in2_d) = delay[3] (mul(in1, in2), in2)
out_pipelined = delay[1] add(tmp, in2_d)
out_live = delay[1] add(tmp, delay[0] in2)
}
flow accum_alu(in1, in2) -> (out) {
reg total::[init=0]
let tmp = delay[3] mul(in1, in2)
let sum = add(total, prod) // ERROR total and prod different times
let sum_aligned = add(delay[0] total, tmp)
next(total) = delay[1] sum_aligned
out = total
}
Option 5:
pipe mul(a, b) -> (c) { c = a * b }
pipe add(a, b) -> (c) { c = a + b }
flow alu(in1, in2) -> (out_pipelined, out_live) {
let (tmp@3, in2_d@3) = delay[3] (mul(in1, in2), in2)
out_pipelined = delay[1] add(tmp@3, in2_d@3)
out_live = delay[1] add(tmp@3, in2@0) // explicit timing annotation
}
flow accum_alu(in1, in2) -> (out) {
reg total::[init=0]
let tmp@3 = delay[3] mul(in1, in2)
let sum_aligned = add(total@0, tmp@3) // explicit timing makes alignment clear
next(total) = delay[1] sum_aligned
out = total@0 // current register output
}
// Check that e1 and e2 have the same delays since inputs start
assert_delay(e1 == e2)