Using guarded atomic actions is an old and well-loved design paradigm.
The term `wiring' above is used in the sense of TLM models: binding initiators to target methods.
The intention was that a compiler can direct scheduling decisions to span various power/performance implementations for a given program.
But designs with an over-reliance on shared variables suffer RaW/WaR hazards when the schedule is altered. »LINK: Small Examples»Toy BSV Compiler (DJG)
First basic example: two rules: one increments, the other exits the simulation. This example looks very much like RTL: provides an easy entry for hardware engineers.
module mkTb1 (Empty); Reg#(int) x <- mkReg (23); rule countup (x < 30); int y = x + 1; // This is short for int y = x.read() + 1; x <= x + 1; // This is short for x.write(x.read() + 1); $display ("x = %0d, y = %0d", x, y); endrule rule done (x >= 30); $finish (0); endrule endmodule: mkTb1The problem with this example is that nice atomic rules are acting on a nasty mutable shared variable (the register). In general, RAMs and registers cannot be shared by freely-schedullable rules owing to RaW hazards and the like.
It is much nicer if rules communicate with FIFOs, like the CSP process networks.
Our second example shows a FIFO-like pipe that is acted on by two rules. This is immune from schedulling artefacts/hazards.
The example interface is for pipeline object that could have arbitrary delay. Sending process is blocked by implied handshaking wires (hence far less typing than Verilog) and in the future would allow the programmer or the compiler to re-time the implementation of the pipe component.
module mkTb2 (Empty); Reg#(int) x <- mkReg ('h10); Pipe_ifc pipe <- mkPipe; rule fill; pipe.send (x); x <= x + 'h10; // This is short for x.write(x.read() + 'h10); endrule rule drain; let y = pipe.receive(); $display (" y = %0h", y); if (y > 'h80) $finish(0); endrule endmodule
Its advanced generative elaborator is a functional language and a joy to use for advanced/functional programmers. So it is/was much nicer to use than pure RTL.
It has a scheduler (cf DBMS query planner) and a behavioural-sub language for when imperative is best.
Like Chisel, it has good support for valid-tagged data in registers and busses. Hence compiler optimisations that ignore dead data are potentially possible.
As said, the main shortcoming of Bluespec is/was that the nice guarded atomic actions normally operate on imperative objects such as registers and RAMs where WaW/RaW/WaR bites as soon as transaction order is not carefully controlled.
Also, imperative expression using a conceptual thread is also much loved by programmers, so Bluespec has a behavioural sub-language compiler built in that generates state machines.
|45: (C) 2012-18, DJ Greaves, University of Cambridge, Computer Laboratory.|