580 likes | 706 Views
Balsa: A Language Tutorial. Dr. Doug Edwards doug@cs.man.ac.uk. Structural Iteration. constant n = 8 procedure buffer_n (input i : word; output o : word) is local array 1 .. n-1 of channel c : word begin buffer (i, c[1]) || -- first buffer buffer (c[n-1], o) || -- last buffer
E N D
Balsa: A Language Tutorial Dr. Doug Edwards doug@cs.man.ac.uk
Structural Iteration constant n = 8 procedure buffer_n (input i : word; output o : word) is local array 1 .. n-1 of channel c : word begin buffer (i, c[1]) || -- first buffer buffer (c[n-1], o) || -- last buffer for || i in 1 .. n-2 then -- i’th buffer buffer (c[i], c[i+1]) end end declare constant internal arrayed channels compose n-2 internal buffers parallel structural iteration
Micropipeline Buffer procedure buffer (input i : byte; output o : byte) is local variable x : byte begin loop select i then x := i -- store the data end; o <- x end end select “chooses” a single input resulting in input “i” being passive select encloses assignment select statement has enclosed semantics
Examples: Simple Counters • Circuits count handshakes on their I/Ps and produce an O/P count bundle • They illustrate: • strong data typing • data types • arrays, records, enumeration • control structures • if, case • shared procedures
Modulo-16 binary counter procedure count16 (sync aclk; output count : nibble) is local variable count_reg : nibble begin loop sync aclk ; count <- count_reg ; count_reg := ( count_reg + 1 as nibble) end end handshake only input 4-bit output await h/s on input then output count from internal register inc internal register assign result back to internal variable cast result back to correct size
Passive Input Counter procedure count16 (sync aclk; output count : nibble) is local variable count_reg : nibble begin loop count <- count_reg ; select aclk then continue end ; count_reg := ( count_reg + 1 as nibble) end end using select makes aclk a passive i/p
Modulo-10 counter procedure count10(sync aclk; output count: C_size) is local variable count_reg : C_size variable tmp : C_size begin loop sync aclk ; if count_reg /= max_count then tmp := (count_reg + 1 as C_size) else tmp := 0 end || count <- count_reg ; count_reg := tmp end -- loop end end C_size and max_count previously declared temp variable updated in parallel with O/P assignment if then else construct
Nested if Statements • Balsa does not have an elseif keyword if condition1 then procA else if condition2 then procB end else procC end • Note the explicit sequencing
Parallel Guard Evaluation • If the guards are mutually exclusive, conditions may be tested in parallel: if condition1 then procA | condition2 then procB else procC end
Loadable Decade Counter -1 • Illustrates record data type: -- count10b.balsa: an aysnchronous loadable decade counter import [balsa.types.basic] public type C_size is nibble constant max_count = 9 type In_bundle is record ld_data : C_size ; ld_ctrl : bit end in-line comment record data-structure definition
Loadable Decade Counter -2 procedure updown10 (input in_sigs: In_bundle; output count: C_size) is local variable count_reg : C_size variable tmp : C_size begin loop -- main procedure body end end body in next slide
Recordselector Recordselector Loadable Decade Counter -3 select in_sigs then if in_sigs.ld_ctrl = 0 then count_reg := in_sigs.ld_data else if count_reg /= max_count then tmp := (count_reg + 1 as C_size) else tmp := 0 end || count <- count_reg ; count_reg := tmp end -- end if end -- complete select H/S load counter enclosed select allows multiple reads from I/P bundlewithout latches increment count or wrap round
Padding Record Structures • Records can be coerced to a fixed length: type Flags is record carry, overflow, zero, negative, int_en: bit over byte Flags is padded to 8 bits
Up/Down Counter • Add another bit to control direction of counting: type C_size is nibble constant max_count = 9 type In_bundle is record ld_data : C_size ; ld_ctrl : bit; dir : bit end direction bit
Up/Down Counter case in_sigs.dir of 0 then -- counting down if count_reg /= 0 then tmp := (count_reg - 1 as C_size) else tmp := max_count end || count <- count_reg | 1 then -- counting up if count_reg /= max_count then tmp := (count_reg + 1 as C_size) else tmp := 0 end || count <- count_reg end ; -- end case statement case statement count down or count up
Enumeration • Up/down control signal could be defined as: type dir is enumeration down, up end • Values may be aliased type opcode is enumeration ADD, ADC, SUB, SBC, AND, OR, LD, ST=LD, BR over 3 bits aliased values
Shared Procedures -1 type inc is 2 signed bits procedure updown10 (input in_sigs: In_bundle; output count: C_size) is local variable count_reg : C_size variable tmp : C_size variable inc : inc -- define shared procedure shared update_ctr is begin tmp := (count_reg + inc as C_size) end -- procedure body inc takes values -1 and +1 use shared hardware for counting up and down shared procedure cast result to correct size inc can be +/- 1
Shared Procedures -2 loop select in_sigs then begin if in_sigs.load_l = 0 then count_reg := in_sigs.ld_data else case in_sigs.dir of down then -- counting down inc := ( -1 as inc); if count_reg /= 0 then update_ctr() else tmp := max_count similar code for count up counting down: set inc to -1 cast required call shared procedure
Arrays • Numerically indexed compositions of the same type • Typical use is in the specification of register banks type RegData is 16 bits variable RegBank : array 0..7 of RegData
Register Bank Definition bundle containing Control info type RegCtrl is record Read0 : RegNo ; Read1 : RegNo ; Write : RegNo ; DoRead0 : bit ; -- if true read port 0 DoRead1 : bit ; -- if true read port 1 DoWrite : bit -- if true write data end procedure RegBank ( input RegCtrl : RegCtrl; input WritePort : RegData ; output ReadPort0, ReadPort1 : RegData ) is local variable RegBank : array 0..7 of RegData Register identifiers read/write control bits Control word write data read data internal registers
Register Bank Control write signalled- await data, then update register await control word loop select RegCtrl then if RegCtrl.DoWrite then select WritePort then RegBank[RegCtrl.Write] := WritePort end else if RegCtrl.DoRead0 then ReadPort0 <- RegBank[RegCtrl.Read0] end || if RegCtrl.DoRead1 then ReadPort1 <- RegBank[RegCtrl.Read1] end end end end two reads can occur together can’t read and write simultaneously read register & output to channel
Array Operations • Arrays can be concatenated variable a, b : byte variable c: array 4 of byte variable d : array 6 of byte c:= {a,b,a,b} -- tuple construction c:= {a,b} @ {a,b} -- array concatenation d:= c @ {a,b}
Array Operations • arrays can be sliced variable x : array 0 ..7 of 4 bits variable y : array 0.. 1 of 4 bits y:= x[1..2] -- returns a slice of x of type-- array 0 .. 1 of 4 bits
Instruction Decoder type Word is 16 signed bits type Imm5 is 5 signed bits variable ICtrl is 8 signed bits -- bottom 5 bits contain an immediate value variable Imm16 : word Im16:= (((ICtrl as array 0..7 of bit)[0..4] as Imm5) as Word) Word and Imm5 are signed bits 5-bit field is extracted & sign-extended to 16 bits
Instruction Decoder type Word is 16 signed bits type Imm5 is 5 signed bits variable ICtrl is 8 signed bits -- bottom 5 bits contain an immediate value variable Imm16 : word Im16:= (ICtrl as array 0..7 of bit) casts Ictrl into an array of bits for slicing
Instruction Decoder type Word is 16 signed bits type Imm5 is 5 signed bits variable ICtrl is 8 signed bits -- bottom 5 bits contain an immediate value variable Imm16 : word Im16:= (ICtrl as array 0..7 of bit)[0..4] extract bottom 5 bits
Instruction Decoder type Word is 16 signed bits type Imm5 is 5 signed bits variable ICtrl is 8 signed bits -- bottom 5 bits contain an immediate value variable Imm16 : word Im16:= ((ICtrl as array 0..7 of bit)[0..4] as Imm5) cast the extracted bits into a 5-bit signed number
Instruction Decoder type Word is 16 signed bits type Imm5 is 5 signed bits variable ICtrl is 8 signed bits -- bottom 5 bits contain an immediate value variable Imm16 : word Im16:= (((ICtrl as array 0..7 of bit)[0..4] as Imm5) as Word) sign-extend the 5-bit number to 16 bits
The Select Statement • MUX: • Illustrates unbuffered choice • Inputs assumed mutually exclusive • No internal latches procedure mux (input a, b :byte; output c :byte) is begin loop select a then c <- a -- channel behaves like a variable | b then c <- b -- ditto end end end select gives choice
Arbitrated Choice public procedure mux (input a, b :byte; output c :byte) is begin loop arbitrate a then c <- a | b then c <- b end end end arbitrated choice
Parameterised Procedures • Facilitates libraries • Ex: buffer with parameterised width procedure Buffer ( parameter X : type ; input i : X; output o : X) is local variable x : X begin loop i -> x ; o <- x end end X is of type type vars defined in terms of parameterised type
Using Parameterised Modules -- pbuffer1a.balsa - calling parameterised a procedure import [balsa.types.basic] import [pbuffer1] public -- instantiate a byte-wide single place buffer procedure test (input a :byte ; output b : byte) is begin Buffer over type byte of a,b end invoke a buffer of width byte Buffer defined previously
Multiple Parameters -1 • Consider a pipeline • with parameterised width • with parameterised depth -- BufferN: a n-place parameterised, variable width buffer procedure BufferN( parameter n : cardinal ; parameter X : type ; input i : X ; output o : X) is local procedure buffer is Buffer over type X begin -- body of procedure end type cardinal ranges over natural numbers
test for special cases test for special cases Multiple Parameters -2 if n = 1 then -- single place pipeline buffer(i, o) | n >= 2 then local array 1 .. n-1 of channel c : X begin buffer(i, c[1]) || -- first buffer buffer(c[n-1], o) || -- last buffer for || i in 1 ..n-2 then buffer(c[i], c[i+1]) end end else print error, “zero length pipeline specified” end Procedure Buffer8 is BufferN over 4, type byte body of procedure define a 4-deep byte-wide pipeline
Recursive Procedures • Adding recursion to Balsa allows elegant specifications of many circuits • Consider circuit to generate n handshakes for each call • Divide circuit into odd and even cases • even case: call itself twice • odd case : issue preliminary h/s and then call itself twice
Handshake Generator -1 procedure Repeat (parameter n : cardinal; sync o ) is begin if n = 0 then print error, “Repeat n must not be 0” | n = 1 then sync o | n = 2 then sync o ; sync o else -- main body of procedure end procedure name “repeat” base cases recursive definition here
Handshake Generator -2 local shared doNext is begin Repeat over n/2 of (o) end begin if (n as bit) then -- n is odd sync o end ; doNext () ; doNext () end end Procedure Gen5 is Repeat over 5 define local shared procedure recursive call of “repeat” call shared procedure twice define a 5x generator
Handshake Multiplier -- MultHS: repeat: handshake on `o’ `n’ times for each input import [balsa.types.basic] import [GenHS] public procedure MultHS (parameter n : cardinal; sync i ; sync o ) is begin loop select i then continue end ; Repeat over n of (o) end end -- Here is a x5 gnerator procedure MultHS5 is MultHS over 5 procedure repeat defined earlier
An n-way multiplexer • Decompose MUX:
An n-way multiplexer -1 -- Pmux1.balsa: A recursive parameterised MUX definition import [balsa.types.basic] public procedure PMux ( parameter X : type; parameter n : cardinal; array n of input inp : X; output out : X ) is begin -- procedure body width of input number of inputs each input is a channel output channel
An n-way multiplexer -2 if n = 0 then print error,”Parameter n should not be zero” | n = 1 then loop select inp[0] -> inp then out <- inp end end | n = 2 then loop select inp[0] -> inp then out <- inp | inp[1] -> inp then out <- inp end end when data arrives on either i/p, pass it to o/p base cases
An n-way multiplexer -3 else local channel out0, out1 : X constant mid = n/2 begin PMux over type X, mid of inp[0..mid-1],out0 || PMux over type X, n-mid of inp[mid..n-1],out1 || PMux over type X, 2 of {out0,out1},out end end end 2 internal channels two half-size muxs & one 2:1 mux
Systolic Counters -1 • Kees van Berkel’s design: • Recursively split counter into a head cell & a n/2 tail cell • Head cells may be be odd or even counts
Systolic Counters -2 • Example: • A modulo-11 counter may be constructed 11 = 1 + 2 * 5 11 = 1 + 2 * (1 + 2 * 2) 11 = 1 + 2 * (1 + 2 * (2 * 1))
Systolic Counters -3 • Count even cell: procedure c_even(sync a_left, a_right, b_left, b_right) is begin loop select a_right then sync a_left ; sync a_left | b_right then sync b_left end end end Choose between a or b h/s on right if a then double else pass b from right to left
Systolic Counters -4 • Count odd cell procedure c_odd(sync a_left, a_right, b_left, b_right) is begin loop sync a_left; select a_right then sync a_left | b_right then sync b_left end end end first h/s on left then choose bewteen a & b and pass on
Systolic Counters -5 • Base case: count 1 cell procedure c_1(sync a, b) is begin loop sync a; sync b end end copy handshake from a to b
Systolic Counters -6 procedure countN (parameter N : cardinal ; sync a, b) is local sync a_int, b_int begin if N = 0 then print error, “Parameter n should not be zero” | N = 1 then c_1(a, b) else if (N as bit) then -- Odd c_odd(a, a_int, b, b_int) else c_even(a, a_int, b, b_int) end || countN over N/2 of a_int, b_int end end -- OK instantiate an arbitrary counter procedure Ctest is countN over 53 2 internal sync- only channels plant either an odd or even channel this is how we use use the counter definition and compose with remainder of counter
Systolic Counters -7 • The count even cell chooses between a and b with a handshake on its right
Count-Even cell • Count even cell: procedure c_even(sync a_left, a_right, b_left, b_right) is begin loop select a_right then sync a_left ; sync a_left | b_right then sync b_left end end end handshake right encloses rest of handshakes