(*
* CBG SQUANDERER EVENT DRIVEN SIMULATOR cbgeds.sml
*
* A basic EDA event driven simulator written in sml/mosml.
* (C) 1999 DJ Greaves.
*
*)
(*--------------------------------------------------------*)
(*
* Datastructure for the model and netlist.
*
*
* A net has a string name and a width.
* A net may be high z, dont know or contain an integer from 0 up to 2**width - 1.
* A net has a list of driving and reading models.
* A model has a unique instance name, a delay, a form and a list of nets it has contact with.
* It also may have some internal state, held in the last field.
*)
datatype value_t = V_n of int | V_z | V_x;
datatype m_t = M_AND | M_OR | M_INV | M_XOR | M_DFF | M_BUFIF | M_TLATCH | M_CLOCK;
datatype internal_state_t =
IS_NONE
| IS_DFF of value_t ref
| IS_CLOCK of int ref
;
datatype
net_t = NET of value_t ref * string * int * model_t list ref * model_t list ref
and
model_t = MODEL of string * int * m_t * net_t list * internal_state_t (*hardcoded*)
(* Also need a soft-coded model with a frament of imperative language inside it. *)
;
(* Constructor for a new net *)
fun net(s, w) = NET(ref V_x, s, w, ref nil, ref nil);
(* Function to set the new value of a net and return a list of
* any models that must be executed because they are sensitive to
* changes in this net.
*)
fun net_setvalue(NET(value, s, w, drivers, sensitives), new_value) =
if (!value = new_value) then nil
else
(
value := new_value;
!sensitives
)
;
(* Function to read current value of a net *)
fun netvalue(NET(value, s, w, drivers, sensitives)) = !value
;
(* Constructor for a new model. Connects itself to each net in use. *)
fun model(s, d, form, output_nets, input_nets) =
let val internal_state =
if (form = M_DFF) then IS_DFF(ref V_x) else
if (form = M_CLOCK) then IS_CLOCK(ref 0) else
IS_NONE
val nets = output_nets @ input_nets
val m = MODEL(s, d, form, nets, internal_state)
fun connect_output(NET(v, s, w, drivers, sensitives)) = drivers := m :: (!drivers)
fun connect_input(NET(v, s, w, drivers, sensitives)) = sensitives := m :: (!sensitives)
val _ = app connect_output output_nets
val _ = app connect_input input_nets
in m
end
;
(*--------------------------------------------------------*)
(*
* Datastructure for an event list and also the current global time, tnow.
*
* The fields of an event are the time that it occurs, the net it changes and
* the new value on that net. It also has a pointer to the next event in time
* order.
*)
type time_t = int;
val tnow = ref 0;
datatype event_t =
EVENT of time_t * net_t * value_t * event_t ref
| EMPTY
;
val eventlist = ref EMPTY;
(* Constructor for a new event that also inserts it at the correct point in the sorted event list. *)
fun event(time, net, value) =
let fun a e = case !e of
(A as EMPTY) => e := EVENT(time, net, value, ref A)
| (A as EVENT(t, n, v, e')) => if (t > time)
then e := EVENT(time, net, value, ref A)
else a e'
in a eventlist
end
;
(*--------------------------------------------------------*)
(*
* Some code for example models is provided here. These models
* are just the standard logic gates found in most digital
* electronic systems.
*)
fun
example_models(MODEL(iname, delay, M_INV, nets, _)) =
let val input = hd(tl nets)
val output = hd nets
fun invert (V_n 0) = V_n 1
| invert (V_n 1) = V_n 0
| invert (other) = V_x
in event(!tnow+delay, output, invert (netvalue input))
end
| example_models(MODEL(iname, delay, M_AND, nets, _)) =
let val i1 = hd(tl(tl nets))
val i2 = hd(tl nets)
val output = hd nets
fun AND (V_n 0, _) = V_n 0
| AND (_, V_n 0) = V_n 0
| AND (V_n 1, Vn_1) = V_n 1
| AND (other) = V_x
in event(!tnow+delay, output, AND (netvalue i1, netvalue i2))
end
| example_models(MODEL(iname, delay, M_OR, nets, _)) =
let val i1 = hd(tl(tl nets))
val i2 = hd(tl nets)
val output = hd nets
fun OR (V_n 1, _) = V_n 1
| OR (_, V_n 1) = V_n 1
| OR (V_n 0, Vn_0) = V_n 0
| OR (other) = V_x
in event(!tnow+delay, output, OR (netvalue i1, netvalue i2))
end
| example_models(MODEL(iname, delay, M_DFF, nets, IS_DFF(old_clk))) =
let val d = netvalue(hd(tl(tl nets)))
val clk = netvalue(hd(tl nets))
val output = hd nets
(* Positive edge detector here *)
fun posedge (V_n 1, V_n 0) = true
| posedge (_, _) = false
val active_edge = posedge(clk, !old_clk)
val _ = old_clk := clk
in if (active_edge) then event(!tnow+delay, output, d) else ()
end
| example_models(MODEL(iname, delay, M_CLOCK, nets, IS_CLOCK(state))) =
let val output = hd nets
val nv = 1 - (!state)
val _ = state := nv
in event(!tnow+delay, output, V_n nv)
end
| example_models(MODEL(iname, delay, other, nets, _)) =
print ("No model provided for instance " ^ iname ^ "\n")
;
(*--------------------------------------------------------*)
(*
* Dispatcher for a single event. Takes the event from the head of the
* event queue, updates the net and the current time and activates
* all of the models that are sensitive to that net.
*)
fun dispatch_one_event() =
if (!eventlist = EMPTY) then print("simulation finished - no more events\n")
else
let val EVENT(time, net, value, e') = !eventlist
in
(
eventlist := !e';
tnow := time;
app example_models (net_setvalue(net, value))
) end
;
(*--------------------------------------------------------*)
(*
* Top-level simulation
*
*)
fun main() =
(
(* ... put the code to create your circuit here ... *)
(* ... then
Need to add one initial event for each clock and reset generator *)
while (!eventlist <> EMPTY) do dispatch_one_event()
)
;
val _ = quit();
Compute/commit extension...
Either add a list of pending updates to be committed.
Or else a next value field to signal nets and hold a linked list of them.
Repeat:
1. Remove all events that have the current time from the event list head.
2. Dispatch them all (this is the compute phase).
3. Commit next values to current values.
When we repeat the above loop, if there are any zero-delay models, the value of
tnow may not advance: hence a delta cycle.
(* Behavioural code extension... *)
datatype diop_t = D_and | D_or | D_xor;
datatype ex_t =
Num of int
| Net of string
| Inv of ex_t
| Query of ex_t * ex_t * ex_t
| Diadic of diop_t * ex_t * ex_t
| Subscript of ex_t * ex_t
;
(* Imperative commands (might also include a {\tt case} statement) but no loops. *)
datatype cmd_t =
Assign of ex_t * ex_t
| If2 of ex_t * cmd_t
| If3 of ex_t * cmd_t * cmd_t
| Block of cmd_t list
;
(* eof cbg *)