[comp.sys.handhelds] HP48 digital logic simulator

ard@siva.bristol.ac.uk (Tony Duell) (10/15/90)

Here's a digital circuit simulator for the HP48SX. It has 8 internal
primatives: AND, OR, XOR, NOT, BUF (buffer), HI (node tied to logic 1),
LO (node tied to logic 0) and CLK (clock generator of definable high and
low times). It also supports nested macros defined from these elements.

A circuit is entered as a list of lists. Each sublist defines one gate (or
macro-circuit) in the design. The format of the sublist is as follows : 
{"NAME" I1 I2 I3..}
Where NAME is either one of the primatives above or the variable name where the
macro is stored entered as a string. I1 I2 etc are the names of the nodes in
your circuit, and must be valid HP48 name objects (or in the case of "CLK", the
first 2 arguments are integers). For example:
{"AND" A1 A2 Both}  is the representation of the circuit :

  A1----------!\
              ! )--------- Both
  A2----------!/

  In the primatives, the inputs are always given first, and I suggest you do
the same in macros (see below). 

2 - input gates
---------------
There are 3 types of 2-input gate defined, AND, OR and XOR. The formats are

 list format          equivalent expression (which you don't enter)
{"AND" A1 A2 Q}       Q= A1 AND A2
{"OR" A1 A2 Q}        Q= A1 OR A2
{"XOR" A1 A2 Q}       Q= A1 XOR A2

1 - input gates
---------------

There are 2 types of 1-input gate, NOT and Buffer:

 list format        equivalent expression
{"NOT" A B}         B=NOT A
{"BUF" A B}         B=A

Tied nodes
----------
A node can also be defined as permanently high or low : 

 list format    equivalent expression
{"HI" A}        A=1
{"LO" A}        A=0

Clock Generators.
The high and low times of the clock generator are defined in terms of
time-steps which are defined as 1 horizontal pixel on the final timing diagram.
Note that all gates have a propagation delay of one time-step. The format for
defining a clock generator is :
{"CLK" hi_time lo_time Output}
Where hi_time and lo_time are real numbers giving the number of time-steps that
the output will be high or low respectively and Output is the node name for the
output.
e.g. :
{"CLK" 4 4 A} defines that node A will be low for 4 time-steps, high for 4
time-steps, low for 4 time-steps etc.

A circuit is defined by creating a list of these sub-lists e.g.:
{{"CLK" 2 2 A} {"CLK" 4 4 B} {"AND" A B C}}

The only commands you need to know to run the simulator are as follows:

COMPILE
-------
Takes the variable name where a circuit is stored in top of stack, and sets up
the internal variables to represent that circuit. Use this before attempting to
simulate

NODES
-----
Takes a list of nodes which are to appear in the final timing diagram in top of
stack, and stores it in the internal variable OUT.L

ALL
---
sets up OUT.L to contain _ALL_ nodes in the circuit (including the internal
ones in macros). Only useful for simple circuits.

SIM
---
Takes a length of simualtion in top of stack (a real number, max 100) and plots
the timing diagram (in PICT) for the given circuit. After it has finished,
press any key (except ATTN) to exit and restore the stack display 

Example : 
Given the circuit defined above in top of stack, store it in 'CSH'
then execute the following
'CSH' COMPILE            set up this circuit
ALL                      we'll show all nodes
100 SIM                  draw a timing diagram.


MACROS
------
A macro-circuit is a bit like a subroutine - it's a part of a circuit (e.g. a
half-adder or a flip-flop) which is used several times. For an example, see the
variable HADD in the directory which contains the macro that defines a half
adder.
Each macro definition has the form
{{External list} {{GATE1}{GATE2}...}}
where {{GATE1}{GATE2}...} is the circuit description of the macro as defined
above, and {External list} is the list of external connections.
e.g. the 2-input multiplexor (variable MUX) in the directory

Circuit 
           !\
SEL --+----! >o------------!\
      !    !/              ! )--!
      !             A------!/   ------\----\
      !                                )    )-------- Y
      !             B------!\   ------/----/
      !                    ! )--!
      !--------------------!/


Now, the external connections are SEL, A, B and Y

           !\     NS
SEL --+----! >o------------!\
      !    !/              ! )--! YA
      !             A------!/   ------\----\
      !                                )    )-------- Y
      !             B------!\   ------/----/
      !                    ! )--! YB
      !--------------------!/


I've now labeled the internal nodes (NS, YA, YB)

Thus, we get the definition:
{{SEL A B Y}{{"NOT" SEL NS} {"AND" A NS YA} {"AND" B SEL YB} {"OR" YA YB Y}}}

If this is stored in the variable MUX , the element
{"MUX" X Y Z GO} will be valid, with the correspondence:
X -> SEL
Y -> A
Z -> B
GO -> Y

circuits (and other macros) can be defined in terms of MUX.


Example : the full adder
------------------------
I have included a simple example to demonstrate the features of this logic
simulator : the single-bit full adder.
In the variable HADD is the macro for a half - adder, whereas in FADD, I have
given the macro for a full-adder, defined in terms of FADD
In the variable DEMO, I have defined a simple circuit with one full-adder, with
its 3 input driven from clock-generators in the ratio 1:2:4. Here's how to run
the demonstration:
'DEMO' COMPILE      define the circuit
{M N P S C} NODES   only display the inputs and outputs
100 SIM             display the timing diagram (after it's finished, press any
                    key to exit)


I am quite willing to answer questions about how this program works, but as
it's likely to be quite long, I'll only post it to the net if there is
sufficient interest.....
:-)

-Tony Duell
ARD @ UK.AC.BRIS.PVA  (JANET)
ARD @ SIVA.BRIS.AC.UK (BITNET)

(This program and its documentation are public domain. They may be freely
copied, used and published in user-group newsletters provided that my name
remains attached to them. They may not be sold for profit)

--------->8---------------->8----------------->8------------->8----------
%%HP: T(3)A(R)F(.);
DIR
  DEMO { { "CLK" 5
5 M } { "CLK" 10 10
N } { "CLK" 20 20 P
} { "FADD" M N P S
C } }
  FADD { { A B CIN
SUM COUT } { {
"HADD" A B S1 C1 }
{ "HADD" CIN S1 SUM
C2 } { "OR" C1 C2
COUT } } }
  HADD { { A B SUM
CARRY } { { "XOR" A
B SUM } { "AND" A B
CARRY } } }
  SIM
    \<< INIT TITLE 1
SWAP
      FOR j
NXTSTATE 1 OUT.L
SIZE
        FOR i OUT.L
i GET NAME.L SWAP
POS STATE SWAP GET
-4 * 6 + i 1 - 8 *
+ R\->B j 29 + R\->B
SWAP 2 \->LIST PIXON
        NEXT
      NEXT 7 FREEZE
0 WAIT DROP TEXT
    \>>
  ALL
    \<< NAME.L NODES
    \>>
  NODES
    \<< 'OUT.L' STO
    \>>
  COMPILE
    \<< NEWC RCL DUP
SIZE \-> cir siz
      \<< 1 siz
        FOR i cir i
GET COMGATE
        NEXT
      \>>
    \>>
  MUX4 { { A B C D
SA SB Y } { { "MUX"
SA A B YA } { "MUX"
SA C D YB } { "MUX"
SB YA YB Y } } }
  MUX { { SEL A B Y
} { { "NOT" SEL NS
} { "AND" A NS YA }
{ "AND" B SEL YB }
{ "OR" YA YB Y } }
}
  MACCNT 3
  TITLE
    \<< ERASE { # 1Eh
# 0h } { # 1Eh
# 3Fh } LINE OUT.L
SIZE 8 MIN 1 SWAP
      FOR i OUT.L i
GET \->STR DUP SIZE 1
- 2 SWAP SUB DUP
SIZE 5 MIN 1 SWAP
SUB 2 \->GROB i 1 - 8
* R\->B # 0h SWAP 2
\->LIST SWAP PICT 3
ROLLD GOR
      NEXT { # 0h
# 0h } PVIEW
    \>>
  PPAR {
(-6.5,-3.1)
(6.5,3.2) X 0 (0,0)
FUNCTION Y }
  OUT.L { M N P S C
}
  NXTSTATE
    \<< 1 CIRCUIT.L
SIZE
      FOR i
CIRCUIT.L i GET DUP
1 GET "D" SWAP +
OBJ\->
      NEXT
'STATE.CNT' INCR
DROP STATE.NEW
'STATE' STO
    \>>
  TMP { { "CLK" 5 5
R } { "CLK" 10 10 Q
} { "CLK" 20 20 P }
{ "MUX" P Q R S } }
  STATE { 1 1 0 0 1
0 0 1 }
  DCLK
    \<< DUP DUP 2 GET
SWAP 3 GET \-> n m
      \<< STATE.CNT n
m + MOD n \>= SWAP 4
GET SWAP STATE.NEW
3 ROLLD PUT
'STATE.NEW' STO
      \>>
    \>>
  DLO
    \<< 2 GET
'STATE.NEW' SWAP 0
PUT
    \>>
  DHI
    \<< 2 GET
'STATE.NEW' SWAP 1
PUT
    \>>
  DXOR
    \<< DUP 2 GET
STATE SWAP GET OVER
3 GET STATE SWAP
GET XOR SWAP 4 GET
STATE.NEW 3 ROLLD
SWAP PUT
'STATE.NEW' STO
    \>>
  DOR
    \<< DUP 2 GET
STATE SWAP GET OVER
3 GET STATE SWAP
GET OR SWAP 4 GET
STATE.NEW 3 ROLLD
SWAP PUT
'STATE.NEW' STO
    \>>
  DAND
    \<< DUP 2 GET
STATE SWAP GET OVER
3 GET STATE SWAP
GET AND SWAP 4 GET
STATE.NEW 3 ROLLD
SWAP PUT
'STATE.NEW' STO
    \>>
  DNOT
    \<< DUP 2 GET
STATE SWAP GET NOT
SWAP 3 GET
STATE.NEW 3 ROLLD
SWAP PUT
'STATE.NEW' STO
    \>>
  DBUF
    \<< DUP 2 GET
STATE SWAP GET SWAP
3 GET STATE.NEW 3
ROLLD SWAP PUT
'STATE.NEW' STO
    \>>
  STATE.CNT 100
  STATE.NEW { 1 1 0
0 1 0 0 1 }
  INIT
    \<< NAME.L SIZE
CLIST DUP 'STATE'
STO 'STATE.NEW' STO
0 'STATE.CNT' STO
    \>>
  COMGATE
    \<< DUP SIZE OVER
1 GET PRIM OVER POS
      IF 0 ==
      THEN MACRO
      ELSE 1 \->LIST
SWAP 2 SWAP
        FOR i OVER
i GET DUP TYPE
          IF 6 ==
          THEN
ADDNAME
          END 1
\->LIST +
        NEXT SWAP
DROP 1 \->LIST
CIRCUIT.L SWAP +
'CIRCUIT.L' STO
      END
    \>>
  ADDNAME
    \<< NAME.L OVER
POS DUP
      IF 0 ==
      THEN DROP
NAME.L SWAP + DUP
'NAME.L' STO SIZE
      ELSE SWAP
DROP
      END
    \>>
  NEWC
    \<< { } DUP
'CIRCUIT.L' STO
'NAME.L' STO 0
'MACCNT' STO
    \>>
  CIRCUIT.L { {
"CLK" 5 5 1 } {
"CLK" 10 10 2 } {
"CLK" 20 20 3 } {
"XOR" 1 2 4 } {
"AND" 1 2 5 } {
"XOR" 3 4 6 } {
"AND" 3 4 7 } {
"OR" 5 7 8 } }
  NAME.L { M N P
S1.1 C1.1 S C2.1 C
}
  MACRO
    \<< DROP DROP
OBJ\-> 1 - \->LIST
'MACCNT' INCR \->
arglist locmac
      \<< OBJ\-> OBJ\->
DROP \-> extrn circ
        \<< 1 circ
SIZE
          FOR i
circ i GET 2 OVER
SIZE
            FOR j
DUP j GET DUP TYPE
              IF 6
==
              THEN
extrn OVER POS
IF 0 ==
THEN \->STR 1 OVER
SIZE 1 - SUB "." +
locmac \->STR + "'" +
OBJ\-> j SWAP PUT
ELSE extrn SWAP POS
arglist SWAP GET j
SWAP PUT
END
              END
            NEXT
COMGATE
          NEXT
        \>>
      \>>
    \>>
  PRIM { "AND" "OR"
"XOR" "NOT" "BUF"
"HI" "LO" "CLK" }
  CLIST
    \<< { } SWAP 1
SWAP
      START { 0 } +
      NEXT
    \>>
END