202 lines
5.6 KiB
Text
202 lines
5.6 KiB
Text
(* File blocks.ml
|
|
|
|
A block is just a black box with nin input lines and nout output lines,
|
|
numbered from 0 to nin-1 and 0 to nout-1 respectively. It will be stored
|
|
in a caml record, with the operation stored as a function. A value on
|
|
a line is represented by a boolean value. *)
|
|
|
|
type block = { nin:int; nout:int; apply:bool array -> bool array };;
|
|
|
|
(* First we need function for boolean conversion to and from integer values,
|
|
mainly for pretty printing of results *)
|
|
|
|
let int_of_bits nbits v =
|
|
if (Array.length v) <> nbits then failwith "bad args"
|
|
else
|
|
(let r = ref 0L in
|
|
for i=nbits-1 downto 0 do
|
|
r := Int64.add (Int64.shift_left !r 1) (if v.(i) then 1L else 0L)
|
|
done;
|
|
!r);;
|
|
|
|
let bits_of_int nbits n =
|
|
let v = Array.make nbits false
|
|
and r = ref n in
|
|
for i=0 to nbits-1 do
|
|
v.(i) <- (Int64.logand !r 1L) <> Int64.zero;
|
|
r := Int64.shift_right_logical !r 1
|
|
done;
|
|
v;;
|
|
|
|
let input nbits v =
|
|
let n = Array.length v in
|
|
let w = Array.make (n*nbits) false in
|
|
Array.iteri (fun i x ->
|
|
Array.blit (bits_of_int nbits x) 0 w (i*nbits) nbits
|
|
) v;
|
|
w;;
|
|
|
|
let output nbits v =
|
|
let nv = Array.length v in
|
|
let r = nv mod nbits and n = nv/nbits in
|
|
if r <> 0 then failwith "bad output size" else
|
|
Array.init n (fun i ->
|
|
int_of_bits nbits (Array.sub v (i*nbits) nbits)
|
|
);;
|
|
|
|
(* We have a type for blocks, so we need operations on blocks.
|
|
|
|
assoc: make one block from two blocks, side by side (they are not connected)
|
|
serial: connect input from one block to output of another block
|
|
parallel: make two outputs from one input passing through two blocks
|
|
block_array: an array of blocks linked by the same connector (assoc, serial, parallel) *)
|
|
|
|
let assoc a b =
|
|
{ nin=a.nin+b.nin; nout=a.nout+b.nout; apply=function
|
|
bits -> Array.append
|
|
(a.apply (Array.sub bits 0 a.nin))
|
|
(b.apply (Array.sub bits a.nin b.nin)) };;
|
|
|
|
let serial a b =
|
|
if a.nout <> b.nin then
|
|
failwith "[serial] bad block"
|
|
else
|
|
{ nin=a.nin; nout=b.nout; apply=function
|
|
bits -> b.apply (a.apply bits) };;
|
|
|
|
let parallel a b =
|
|
if a.nin <> b.nin then
|
|
failwith "[parallel] bad blocks"
|
|
else { nin=a.nin; nout=a.nout+b.nout; apply=function
|
|
bits -> Array.append (a.apply bits) (b.apply bits) };;
|
|
|
|
let block_array comb v =
|
|
let n = Array.length v
|
|
and r = ref v.(0) in
|
|
for i=1 to n-1 do
|
|
r := comb !r v.(i)
|
|
done;
|
|
!r;;
|
|
|
|
(* wires
|
|
|
|
map: map n input lines on length(v) output lines, using the links out(k)=v(in(k))
|
|
pass: n wires not connected (out(k) = in(k))
|
|
fork: a wire is developed into n wires having the same value
|
|
perm: permutation of wires
|
|
forget: n wires going nowhere
|
|
sub: subset of wires, other ones going nowhere *)
|
|
|
|
let map n v = { nin=n; nout=Array.length v; apply=function
|
|
bits -> Array.map (function k -> bits.(k)) v };;
|
|
|
|
let pass n = { nin=n; nout=n; apply=function
|
|
bits -> bits };;
|
|
|
|
let fork n = { nin=1; nout=n; apply=function
|
|
bits -> Array.make n bits.(0) };;
|
|
|
|
let perm v =
|
|
let n = Array.length v in
|
|
{ nin=n; nout=n; apply=function
|
|
bits -> Array.init n (function k -> bits.(v.(k))) };;
|
|
|
|
let forget n = { nin=n; nout=0; apply=function
|
|
bits -> [| |] };;
|
|
|
|
let sub nin nout where = { nin=nin; nout=nout; apply=function
|
|
bits -> Array.sub bits where nout };;
|
|
|
|
let transpose n p v =
|
|
if n*p <> Array.length v
|
|
then failwith "bad dim"
|
|
else
|
|
let w = Array.copy v in
|
|
for i=0 to n-1 do
|
|
for j=0 to p-1 do
|
|
let r = i*p+j and s = j*n+i in
|
|
w.(r) <- v.(s)
|
|
done
|
|
done;
|
|
w;;
|
|
|
|
(* line mixing (a special permutation)
|
|
mix 4 2 : 0,1,2,3, 4,5,6,7 -> 0,4, 1,5, 2,6, 3,7
|
|
unmix: inverse operation *)
|
|
|
|
let mix n p = perm (transpose n p (Array.init (n*p) (function x -> x)));;
|
|
|
|
let unmix n p = perm (transpose p n (Array.init (n*p) (function x -> x)));;
|
|
|
|
(* basic blocks
|
|
|
|
dummy: no input, no output, usually not useful
|
|
const: n wires with constant value (true or false)
|
|
encode: translates an Int64 into boolean values, keeping only n lower bits
|
|
bnand: NAND gate, the basic building block for all the other basic gates (or, and, not...) *)
|
|
|
|
let dummy = { nin=0; nout=0; apply=function
|
|
bits -> bits };;
|
|
|
|
let const b n = { nin=0; nout=n; apply=function
|
|
bits -> Array.make n b };;
|
|
|
|
let encode nbits x = { nin=0; nout=nbits; apply=function
|
|
bits -> bits_of_int nbits x };;
|
|
|
|
let bnand = { nin=2; nout=1; apply=function
|
|
[| a; b |] -> [| not (a && b) |] | _ -> failwith "bad args" };;
|
|
|
|
(* block evaluation : returns the value of the output, given an input and a block. *)
|
|
|
|
let eval block nbits_in nbits_out v =
|
|
output nbits_out (block.apply (input nbits_in v));;
|
|
|
|
(* building a 4-bit adder *)
|
|
|
|
(* first we build the usual gates *)
|
|
|
|
let bnot = serial (fork 2) bnand;;
|
|
|
|
let band = serial bnand bnot;;
|
|
|
|
(* a or b = !a nand !b *)
|
|
let bor = serial (assoc bnot bnot) bnand;;
|
|
|
|
(* line "a" -> two lines, "a" and "not a" *)
|
|
let a_not_a = parallel (pass 1) bnot;;
|
|
|
|
let bxor = block_array serial [|
|
|
assoc a_not_a a_not_a;
|
|
perm [| 0; 3; 1; 2 |];
|
|
assoc band band;
|
|
bor |];;
|
|
|
|
let half_adder = parallel bxor band;;
|
|
|
|
(* bits C0,A,B -> S,C1 *)
|
|
let full_adder = block_array serial [|
|
|
assoc half_adder (pass 1);
|
|
perm [| 1; 0; 2 |];
|
|
assoc (pass 1) half_adder;
|
|
perm [| 1; 0; 2 |];
|
|
assoc (pass 1) bor |];;
|
|
|
|
(* 4-bit adder *)
|
|
let add4 = block_array serial [|
|
|
mix 4 2;
|
|
assoc half_adder (pass 6);
|
|
assoc (assoc (pass 1) full_adder) (pass 4);
|
|
assoc (assoc (pass 2) full_adder) (pass 2);
|
|
assoc (pass 3) full_adder |];;
|
|
|
|
(* 4-bit adder and three supplementary lines to make a multiple of 4 (to translate back to 4-bit integers) *)
|
|
let add4_io = assoc add4 (const false 3);;
|
|
|
|
(* wrapping the 4-bit to input and output integers instead of booleans
|
|
plus a b -> (sum,carry)
|
|
*)
|
|
let plus a b =
|
|
let v = Array.map Int64.to_int
|
|
(eval add4_io 4 4 (Array.map Int64.of_int [| a; b |])) in
|
|
v.(0), v.(1);;
|