PALWIZ
======

Palwiz is an open-source program to generate JEDEC programming files
for simple GAL chips.

I wrote it because there was no open-source alternative out there, just
a few old programs that only ran under MS-DOS such as PALASM and ABEL.
Admittedly, they do a lot more than palwiz, but that's at the price of
not being able to use them on your favorite UNIX-like operating system,
which may not even use the same CPU. What you lose in features you
gain in freedom.

The basic design came about after looking at the ancient open source
Fortran version of PALASM and seeing how it worked. That was when I
realized that I didn't need anything to optimize equations or expand
XORs or do pin fitting, I just needed something which would work with
the three standard GAL chips (16V8, 20V8, 22V10) in their various
architecture modes.

Yes, it's quick and dirty, and the code is a bit ugly, but it works,
and it's easier than editing the JEDEC programming files by hand,
which is what I was doing at first. I even found some boo-boos in
my first attempts at hand-programming when writing palwiz.

Palwiz is no substitute for having the GAL data sheets. You will need
to have the data sheets and know how to read them. You will also have
to assign input and output pins yourself, and be aware of when an
output will need more input terms than it has. You just won't have to
care what the specific fuse numbers are or have to carefully count the
right column in a line of '1's.


WHAT'S A GAL CHIP?
==================

(Note: this assumes you have some basic familarity with digital circuit
design. If not, there are many books and web sites which can tell you
all about Boolean algebra and TTL chips.)

A GAL chip is a kind of programmable logic chip. First there were PLAs
(Programmable Logic Array), which had a whole bunch of terms went into
lots of AND gates, the output of which were fed into a whole bunch of
OR gates. The inputs to the AND and OR gates are selected by a matrix
of "fuses". These were really fuses, and a programmer would send enough
power through the chip in a certain way to burn each fuse.

The next evolution from PLAs was PAL chips (Programmable Array Logic),
in which each AND gate fed into a single OR gate, with each OR gate
receiving the output of a fixed number of AND gates, usually 8 or so.
This was a good thing because while you were limited to less inputs for
each OR gate, the larger number of AND gates made it possible to simply
duplicate the common terms for multiple OR gates, and the resulting
chip was simpler to program. PAL chips came in many different
configurations, some with active-low outputs, some with active-high
outputs, some with registered outputs, etc. There were a _lot_ of
different types of PAL chips. The names of PAL chips indicated the
number of inputs, the type of output, and the maximum number of
outputs. For example, a "16R6" had 16 inputs, and 6 registered outputs.

The final evolution was the GAL chip (Generic Array Logic), which
made the old PAL chips obsolete. First of all, the old-fashioned PROM
fuse array was replaced with an EEPROM. This meant that chips could be
reprogrammed if you found a bug or didn't need them any more. The second
improvement was having only three different types of chips, instead of
dozens. This was accomplished by making the output stages (called
"macrocells") configurable in multiple ways. Conversion programs were
available to migrate existing programming from all the old PAL chip
types. The namimg was changed slightly because some outputs could be
used as inputs instead, so the first number now referred to the number
of input pairs to the fuse matrix. This was shown by using a "V" (which
probably means "versatile") as the output type in the chip name.

16V8 has up to 16 inputs and up to 8 outputs in 20 pins; 20V8 has
up to 20 inputs and up to 8 outputs in 24 pins; and 22V10 has up
to 22 inputs and up to 10 outputs in 24 pins. Note that some of
these configurations existed in PALs as well, notably from TI. If
the chip type doesn't start with "GAL" or "PALCE" (AMD's name for
GAL), it's probably not reprogrammable like a GAL chip.

There were a few other chip types which have since been osboleted.
These include the 24V10, 26V12, 20RA10 (RA=registered asynchronous),
the PALCE610 (which was effectively a 20V16, but with very
configurable output stages), and the 29M16 (the M is for macrocell?).

The programming for the chips is specified in what is called a
"JEDEC" file, after the name of the standards group for IC chips.
(Specifically, it is JEDEC Standard Number 3A.) It specifies the
state of each fuse, and a few other things like test information.
However, each chip type has its fuse numbers assigned differently,
so a given JEDEC file works with only one chip type and needs a
specialized conversion program to translate for another chip type.

This is where higher-level specification languages come in. Palwiz
is the next level up, where you can specify the pins by name and
create a JEDEC file based on on a description using simple equations.
Above that are hardware specification languages which can
automatically expand and optimize equations, can automatically
generate counters and state machines, and can automatically assign
pins based on a best fit. Beyond that are languages which can
specify the contents of more complex FPGAs (field programmable
gate arrays) and other high-density generic logic chips.


HOW TO USE IT
=============

To build it from source, just type "gcc palwiz.c -o palwiz".

From the command line, it's just "palwiz <filename>", where <filename>
(without the angle brackets) is your PAL description file. The JEDEC
file is sent to standard output, so you will want to use the ">"
redirection to save it to an output file.

The PAL description file consists of multiple statements ending in
semicolons. There are four types of statements: "chip", "pin",
"test", and equations.


CHIP
----

  chip <chipname>;

Example: chip 20V8R;

This must be the first statement. It specifies the type of chip being
programmed. Since 16V8 and 20V8 have "architecture fuses" which change
the way the chip works, each of these gets a separate subtype.

The supported chip types are:

16V8S - 16V8 in "simple" mode
16V8C - 16V8 in "complex" mode
16V8R - 16V8 in "registered" mode
20V8S - 20V8 in "simple" mode
20V8C - 20V8 in "complex" mode
20V8R - 20V8 in "registered" mode
22V10 - 22V10/22CV10 (the 22V10 has no architecture fuses)
26V12 - 26V12/26CV12 (the 26V12 has no architecture fuses)

For each of these chip types, you will want to download the chip's
data sheet to understand how the inputs and outputs work, and what
they can do in each mode.


PIN
---

  pin <pinnum> [!]<pinname>;

Example: pin 20 !ROMCS;

This specifies the name of each pin, and whether it is active low or not.
If a pin is active low, the "!" will automatically invert an equation on
an output pin or select the inverted term from an input pin. Beware of
using the "!" both in the PIN statement and an equation! This will double-
invert the pin, and you will probably get the wrong result!

The 22V10 has two "phantom" pins for equations that don't go to output pins.
The global reset is phantom pin 25, and the global preset is phantom pin 26.


TEST
----

There are three forms of this:

1) test <testvector>;

Example: test N00N00NNNNNN NNNNNNNNNHNN;

This adds a raw test vector to the JEDEC output file. Spaces in the test
vector are removed automatically. The test vector is a list of states for
each pin of the device. For a full description of what states are used in
the test vectors, you will need to look elsewhere, but here is a simple list:

0,1 - set input to 0 or 1
L,H - test input for being high or low
N   - don't test this pin (N is always used on the power and ground pins)
Z   - test this pin for being three-state disabled
C,K - clock input pin: C = low, high, low; K = high, low, high

2) test '[' [!]<pin>[=state] ... '->' [!]<pin>[=state] ']' ;

Example: test [ CLK=C !D0  D1 !D2  OE -> !BA14  BA15 !BA16 ];

One or more input pins are specified, optionally inverted, and one or more
output pins are specified, optionally inverted. The inverted state of the
pin in the pin declaration is also taken into account. A test vector is built
using the correct pin positions for the named pins. Custom pin states can
be defined with the '=' sign, and 0/1/H/L/C/K states are checked for validity
and inverted per the pin declaration.

3) test pin [pin ...] ;

Example: test OEO ROMCS RAMCS LCO;

Generates test vectors for every possible combination of inputs used by
the listed pins. There are some limitations to this. Registered outputs
can not be used, feedback inputs can not be used, output enables are
not included, and more than six or seven inputs will generate a lot of
test vectors.

Equations
---------

  [!]<outpin>[.OE] [:] = 0;
  [!]<outpin>[.OE] [:] = 1;
  [!]<outpin>[.OE] [:] = [!]<inppin> [<operator> [!]<inppin> ...];

  operator is one of "&", "|"

The simple case sets a term to 0 or 1. The more complicated equations are
a bunch of AND equations separated by ORs. Using the "!" on an input pin
selects the inverted input (or the non-inverted input if the pin was
declared with a "!"), and likewise, using a "!" on the output pin inverts
the output (or un-inverts if the pin was declared with a "!"). When too
many OR terms have been specified, an error is reported.

The AND "&" operator has higher precedence than the OR "|" operator.

Because you'll see it this way everywhere else, "/" is accepted everwhere
instead of "!", "*" is accepted instead of "&", and "+" is accepted instead
of "|".

If the output is registered, use ":=" instead of "=". If the output has
an Output Enable from the pin matrix, adding a ".OE" to the output pin name
will select the pin's Output Enable term.

Clock and OE inputs which are not defined with the fuse matrix are implied
and not declared.


EXAMPLE
=======

This example is for an Atari 7800 cartridge bank select. RAM is located
at $4000-$7FFF, bank switched ROM is located at $8000-$BFFF, the bank is
selected by writing anywhere in $8000-$BFFF, and the last bank of ROM
appears at $C000-$FFFF by using OE to disable the bank select address
in favor of pull-up resistors. Pins 1 and 22 are connected together,
pins 13 and 15 are connected together, and pins 17-19 need a pull-up
resistor. Some pins are named (for future expansion) but not used.

// Atari 7800 supercart 128K ROM + 8/16K RAM or 128K ROM + 16K ROM

chip 20V8R;   // 20V8 registered mode

pin  1 CLK;   pin 24 VCC;
pin  2 CLK2;  pin 23 P_23;
pin  3 RW;    pin 22 LCO;
pin  4 P_4;   pin 21 !RAMCS;
pin  5 A15;   pin 20 !ROMCS;
pin  6 A14;   pin 19 BA14;
pin  7 P_7;   pin 18 BA15;
pin  8 D4;    pin 17 BA16;
pin  9 D0;    pin 16 BA17;
pin 10 D1;    pin 15 !OEO;
pin 11 D2;    pin 14 D3;
pin 12 GND;   pin 13 !OE;

 BA16 := D2;
 BA15 := D1;
 BA14 := D0;
ROMCS =  A15 &         RW;
RAMCS = !A15 &  A14 &       CLK2;
 !LCO =  A15 & !A14 & !RW & CLK2;
  OEO = !A14;

// 20V8R needs OE term for non-registered pins
ROMCS.OE = 1;
  LCO.OE = 1;
  OEO.OE = 1;
RAMCS.OE = 1;
 BA17.OE = 1;

// generate automatic test vectors
test OEO ROMCS RAMCS LCO;

// latch test vectors
test [  CLK=C !D0  D1 !D2  OE -> !BA14   BA15  !BA16   ]; // load
test [ !CLK    D0 !D1  D2  OE -> !BA14   BA15  !BA16   ]; // latched
test [  CLK=C  D0 !D1  D2  OE ->  BA14  !BA15   BA16   ]; // load
test [ !CLK   !D0  D1 !D2  OE ->  BA14  !BA15   BA16   ]; // latched
test [  CLK=C !D0  D1 !D2 !OE ->  BA14=Z BA15=Z BA16=Z ]; // load+disable
test [ !CLK    D0 !D1  D2  OE -> !BA14   BA15  !BA16   ]; // latched


CAVEATS
=======

Well, of course this program isn't guaranteed to do anything but take
up space on your hard drive. If a guaranteed correct program is
important to you, you should be paying people money for a program.


TO DO
=====

I'd really rather start all over and make something with a syntax that
is a subset of either PALASM or ABEL than make major changes, but here
are some things that could still be useful:

Checksum generation for output text.

It would be nice if test vector generation could see common AND terms and
generate a single negative case for each input.


Version 1.1 changes (2007-05-08)
================================

* Using the ".OE" term on a registered 16V8/20V8 output pin would
  silently force the pin to be non-registered. An error is now reported.

* Test vector generation now correctly identifies feedback pins, and
  should no longer report an error when an input-only I/O pin is an
  input term for the output pin being tested.

* Registered feedback terms are now inverted before dumping the
  fusemap.


Version 1.2 changes (2007-05-25)
================================

* Command-line parameter errors would not abort the program

* Removed hard tabs from the source code

* added 26V12 support - it may be obsolete, but I have three 26CV12
  chips, and it's basically a 20V10 with more pins (and VCC/GND moved)
  (This has not been tested with an actual chip, but should work.)

Version 1.2.1 changes (2013-10-01)
================================

* Uncommented u_int, etc. so it would compile.

* Cleaned up makefile

Version 1.2.2 changes (2020-01-05)
==================================

* Changed MAXPINS to 28. 26V12 has 28 pins, but apparently I typed in
  the wrong number, and didn't notice because complier warnings weren't
  so great back then.

* Updated makefile, including adding -Werror and C99 compiler options.

* Reformatted code to K&R style formatting with mandatory braces.

* Code now makes extensive use of in-block decalarations, and uses
  stdbool.h for bool type.

Version 1.3 changes (2020-01-  )
================================
- Chip specs are now stored as constant arrays

- The 26V12 data still had some errors, but more importantly, the 26CV12 and
  26V12 chips, despite the similar name are not the same chip!
  The 26V12 has more option fuses and would require a lot of work to support
  its second clock and buried macrocell modes.

- FIXME break up code into more .c modules?


