blob: 6ef11f11124d9ef8a048b17aa054832be8dce248 [file] [log] [blame]
Chaos automaton verilog code description
July 5, 2022
See "chaos_automaton.v"
Description corresponds to repository commit
version 3bb09e932491e929d1709e9d843173a365be1982
(May 10, 2022)
The code itself (last code commit) is from
version 3def96a8c8ec6e66376e22f013cd6296e57859c8
(Aug 22, 2021)
The base cell is "chaos_cell" (line 409)
Each cell contains 64 bits memory in a serial scan chain with one register
for the serial shift and one latch for capturing the value at the cell.
Both register and latch are represented in the verilog code by 2-dimensional
arrays; the serial shift register is called "lutfunc" and the latched value
is called "lutdata".
The cell function is implemented at line 448: Four functions that apply
the latched function to outputs on all four sides of the cell. The functions
are mapped in such a way that for the same 16-bit value produces an output
that is the same relative to the direction of the output; for bit positions
{3, 2, 1, 0}, bit 3 is the input that is in the same direction as the output,
and if the output is oriented to the north, say, then bit 2 is the input
coming from the east side; bit 1 is the input coming from the south side
(opposite direction from the output), and bit 0 is the input coming from the
west side.
The shift register is implemented at lines 464 to 485. This defines the
way the data snake through the 64 bits. Data enter at lutfunc[0][0]
(or, as defined, lutfunc[`NORTH][0]) and exit at lutfunc[3][15]
(lutfunc[`WEST][15]).
A "reset" signal zeros all registers (but does not affect the latches).
A "hold" signal enables the latches; therefore, a complete reset
consists of applying "reset" followed by releasing "hold".
The cells are formed into an array "chaos_array" defined at lines
495 to 619. The number of cells in the array is defined by XSIZE * YSIZE,
defaulting to 20 and 20 (total 400 cells). The array is created by a
"generate" block. Note that each cell has two pins on each side (not
counting the serial data, clock, reset, and hold), one input and one
output. Therefore the wires connecting the cells together are designated
"dconn", "uconn", "lconn", and "rconn", where the direction "d/u/l/r"
indicates the direction of data flow. The connections include the
pins on the outside of the array; so each of these wires is a 2D array
size (X * (Y + 1)) (for uconn and dconn) or size ((X + 1) * Y) (for lconn
and rconn).
The array module defines a data "holding area" of 64 bits, which is enough
to hold data for one cell. This 64 bit array is called "lutdata" in cell
chaos_array. The ends of this array are connected to the ends of the
"lutdata" serial registers in the array of cells, so that it forms a loop.
So if this loop is clocked 401 * 64 (= 25664) times, then all data in the
array returns to its original position. The idea here is that a single
position in the array can be reprogrammed on the fly, by clocking the loop
until the data for the cell to be modified is in the position of the
64-bit holding area (lutdata). Then the cell's data can be read out (if
needed), modified, and written back, then the shift register clocked again
until that cell's data is once again in the correct position of that cell,
and then all data are simultaneously latched.
The holding area data can be read at any time, as its value is placed on
the output wire array "rdata". Data presented on module input wire array
"wdata" can be written into the holding area if the "write" wire is high
on the rising edge of "clk". During a write cycle, the shift register is
not shifted; otherwise, the shift register is shifted by one bit for
every rising edge of "clk".
The simplified holding area read/write mechanism is mapped to the wishbone
bus in the module "chaos_automaton". The module defines the wishbone
base address "BASE_ADR" as hex 30000000, which is the address assigned to
the user project area by the Caravel processor. Since the wishbone bus
is 32 bits and the holding area is 64 bits, the transfer must be done
with two wishbone word writes. Base address + 0 ("CONFIGL") is defined as
the address for the lower 32 bits of the holding area, and base address + 4
("CONFIGH") is defined as the address for the upper 32 bits of the holding
area. Note that there is a separate 64 bit register in "chaos_automaton"
that is copied to the holding register. The copy is always done with all
64 bits copied at one time. The register in "chaos_automaton" can be
written in 8-bit bytes or 16- or 32-bit words, depending on the data
type used in the RISC-V code. The wishbone read/write process is defined
in lines 279 to 328.
The copying from wishbone register to holding area (or vice versa) is
done automatically. The automation involves two additional wishbone
registers, "ADDRESS" at base address + 8, and "XFER" at base address + 12.
The transfer process is defined at lines 330 to 390. The XFER register
is two bits. Bit 0 = "start" and bit 1 = "finish". The ADDRESS is a
cell address in the range 0 to 199 (for a 20 x 20 array). The procedure
is to do:
(1) Apply an address to the ADDRESS register
(2) Apply the "start" transfer bit to the XFER register
This causes the shift register to clock data around the
loop until the data from the cell at the specified address
is in the holding register. At this point, the data from
that cell can be read via the wishbone bus from address
CONFIGL and CONFIGH.
(3) Write new data for the cell into CONFIGL and CONFIGH
(4) Apply the "finish" transfer bit to the XFER register
This causes the shift register to clock data around the
loop until the data in the holding register ends up at
the position of the cell at the specified address.
When shifting has completed, the "hold" bit on all
registers is toggled to latch the new data. Note that
after "start" is applied, the number of cells shifted
is counted and retained; the "finish" uses that value
to determine how many cycles are required to return to
the initial (reset) position.
So this is a two-cycle process that, without disturbing the operation
of the automaton, rotates the data from a specific cell to the output
and then rotates the data back around a full cycle to its original
position, allowing the cell data to be modified between the two cycles.
This arrangement can also be used to rotate data around one cell at a
time for initial programming, by setting the ADDRESS to 1 and applying
the "start" transfer bit 400 times, ending with applying the "finish"
transfer bit to latch the data at the end (see testbench
dv/chaos_test3/chaos_test3.c for an example of a complete load).
Important note: The cell address register "cell_addr" reads only from
wbs_dat_i[7:0], so if XSIZE * YSIZE > 256, then this must be adjusted.
To do: ASIZE is set to 9, so cell_addr has 9 bits; does ASIZE need
to be 8? Else might as well add the additional bits before line 322.
Lastly, the system has to have input/output. It is not clear how best
to connect inputs and outputs of the chaos_automaton. The simplest
solution is to connect the periphery of the cell array to the GPIO
pins. But even a small (-ish) array like 20x20 has 80 inputs and 80
outputs, and there are only 38 GPIO pins available total. And it
would be nice to be able to capture the internal state of the
system at any time (with the known caveat that the internal state is
asynchronous, so difficult to get a meaningful data capture if there
are oscillating inner states). There *are* 128 full-duplex input/
output signals on the logic analyzer---but those are clocked on the
CPU core clock, and so they too cannot capture the exact state of an
asynchronous signal or apply an asynchronous inputs. Inevitably,
some compromises will have to be made.
The array I/O is the vectors data_in[] and data_out[] in chaos_array,
mapped to the endpoints of the uconn, dconn, rconn, and lconn arrays.
Both arrays are divided into four sets of bits in this order:
high bits .... low bits
left right up down
The "compromising" happens at the level of module chaos_automaton.
Line 195 defines how the array outputs connect to the 38 chip GPIO
pins: Contiguous sets of 9, 9, 10, and 10 bits from north, south,
east, and west, respectively (question: should these, and the data_in
and data_out arrays, be in a clockwise order instead?).
The rest of the connections are made in a "generate" block at lines
233 to 260. The logic analyzer controls the direction of the data,
and whether the data is passed to and from the logic analyzer or to
and from the GPIO pins (where they are connected). So the choice of
having I/O connected to the logic analyzer or GPIO pin can be made
on a per-pin basis. The array inputs and outputs which do not connect
to the GPIO connect only to the logic analyzer.
Comment: The arrangement of mapping GPIO bits to the array is pretty
arbitrary; I think it would be useful maybe to allow some alternative
schemes, such as connecting the GPIO bits to every other (almost)
array I/O, and/or to map the GPIO to the upper bank of array I/O
instead of the lower bank, based on some register setting.