(* * 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 *)