Il CAMForth si basa sul dizionario base del Forth, visto finora, ampliandolo
con tutti i comandi indispensabili per il funzionamento corretto della CAM 8.
Questi comandi possono essere raggruppati in comandi di impostazione,
comandi per la dinamica degli esperimenti e i comandi per la
osservazione, diretta o indiretta, dei risultati.
Prima di conoscere i vari comandi, bisogna saper riconoscere le due unità
base della programmazione CAM: le istruzioni e le steplist.
Le prime sono comandi che compiono una lettura o una scrittura di un
particolare registro di tutti gli STEPchips dei moduli selezionati.
L'operazione di default è la scrittura, la lettura deve essere specificata.
Sono istruzioni per esempio:
site-src host scan-io my-buffer event read event-buffer site-src siteLe steplist sono liste collegate di istruzioni. Ci sono due tipi di liste: named e unnamed. L'insieme di istruzioni sopra, seguito dalla parola
*step*, forma una
steplist unnamed.
Le steplist, al momento dell'esecuzione di *step*, vengono trasmesse
dal Forth alla CAM per essere eseguite.
Si può dire che la CAM riceve istruzioni quando viene raggiunto il comando
*step*.
I comandi di impostazione informano la CAM 8 su dimensioni, grandezze e connessioni. Il comando indispensabile in tutti i programmi CAM 8 è
... BY ... SECTOREsso imposta le dimensioni, espresse implicitamente tramite
BY, e
le grandezze, in numero di siti, per ogni dimensione:
256 BY 256 SECTORsignifica impostare uno spazio bidimensionale di 256 siti nella dimensione x e 256 nella dimensione y.
... BY ... SECTOR: semplicemente indica che la corrispondente
dimensione si estende lungo la connessione dei moduli presenti.select.
| Contenuto registro | Ruolo logico |
|---|---|
| 0 | x- |
| 1 | x+ |
| 2 | y- |
| 3 | y+ |
| 4 | z- |
| 5 | z+ |
| 6 | 0 |
| 7 | 1 |
I valori dei bits 6 e 7 forzano i pin corrispondenti a un'uscita
rispettivamente di tutti 0 o tutti 1. Questo è usato principalmente nelle
routine di inizializzazione della macchina e di solito non sono usati in un
esperimento.
Nel momento dell'inizializzazione ogni modulo di una macchina
composta da molti moduli ha un nome unico, conosciuto come module-id.
Usando questo nome possiamo indirizzare ognuno di questi e riprogrammare la
glue.
Ora cerchiamo di realizzare il proposito di trasformare la connessione fisica
della macchina in figura 1b in uno spazio monodimensionale,
cioè rendere la connessione logica equivalente a quella in figura 1a.
Partiamo pensando al modo in cui i moduli dovranno essere ricollegati:
ognuno di questi dovrà avere solo due moduli adiacenti.
Realizziamo questa connessione seguendo la tabella discussa prima.
select 0 module \ seleziono il modulo 0
connect 1 xppc! \ x+ fisico agisce come x+ logico
0 yppc! \ y+ fisico agisce come x- logico
2 xmpc! \ x- fisico non deve entrare in gioco,
\ quindi lo ridireziono come y-
select 1 module \ seleziono il modulo 1
connect 2 xppc! \ x+ posto fuori gioco
1 yppc! \ y+ agisce come x+
0 xmpc! \ x- agisce come x-
select 2 module \ seleziono il modulo 2
connect 3 xppc! \ x+ posto fuori gioco
1 xmpc! \ x- agisce come x+
0 ympc! \ y- agisce come x-
select 3 module \ seleziono il modulo 3
connect 3 xmpc! \ x- posto fuori gioco
1 ympc! \ y- agisce come x+
0 xppc! \ x+ agisce come x-
Analizziamo i nuovi collegamenti.
Dobbiamo fare in modo che uno shift in una delle due sole direzioni possibili
dello spazio modimensionale faccia scorrere i dati attraverso i moduli in modo
che quello che esce a destra del modulo 0 entri a sinistra del modulo 1, quello
che esce da destra di 1 entri a sinistra di 2, e così via fino ad arrivare
al modulo 3, per il quale quello che esce alla sua destra, sempre guardando il
centro dell'anello, entra alla sinista di 0, chiudendo l'anello:
compiendo uno shift circolare di proporzioni adeguate dobbiamo fare in modo di
ritrovarci nelle condizioni iniziali.
Il concetto dello scan è importantissimo nella definizione della
evoluzione dinamica del sistema, poiché esso determina quali siti saranno
sottoposti al processo di update.
Andiamo quindi ad analizzarlo.
Nella CAM 8 l'unità di tempo fondamentale è lo scan, un processo
indivisibile di updating della totalità o di solo una parte dell'array
delle celle, che memorizzano gli stati degli automi.
Uno scan può essere iniziato in due modi:
L'obiettivo di uno scan è normalmente aggiornare le celle secondo le
regole di update degli automi.
Il registro Site-src si occupa di decidere quale tipo di informazione
verrà scritta nella DRAM: potrà essere il risultato di ritorno dalla
LUT, o l'ingresso flywheel, o dati provenienti dall'host e così via.
Questi ingressi per la DRAM sono:
scan-io, il
quale non fa altro che usare il registro SIOR come mezzo di comunicazione con
l'host:
site-src host \ imposta l'host come sorgente per le celle
scan-io init-pattern \ il buffer init-pattern viene letto e scritto
\ nelle celle
Un modo più semplice, se si ha un pattern disponibile in un file, è
usare il comando file>cam "nome.del.pattern", che non fa altro che
appoggiarsi ai comandi sopra citati.lut-src site \ imposta la LUT ad essere indirizzata dalle celle site-src lut \ le celle vengono aggiornate dalla LUTDiverse combinazioni, anche le più strane, possono essere specificate manipolando il registro Site Data Source. Inoltre il comando map viene in nostro aiuto per realizzare delle situazioni molto particolari, come può essere voler rimpiazzare il contenuto delle celle con un XOR tra il valore di ritorno dalla LUT e il contenuto stesso della cella.
site-src site map lut map xor map!In pratica map estrae il valore e lo rende disponibile a una successiva elaborazione, terminata da map!.
0 0 == nord 1 1 == sud 2 2 == est 3 3 == ovest 0 3 == direzioniTramite la parola "==", ai campi compresi tra il numero nella prima posizione e il numero nella seconda, possiamo dare ad essi un nome più indicativo. Riferimenti successivi ai layers possono essere compiuti tramite questi nomi.
La CAM 8 è una macchina ad automi cellulari partizionati: questo significa
che il nuovo valore dei bits di ritorno da un'elaborazione della LUT dipende
solo dal vecchio valore che i bits avevano in quel sito, cosicchè, quando
l'update di tutte le celle sarà terminato, il risultato è
necessariamente indipendente dall'ordine di scan delle celle.
Senza un modo per muovere questi bits all'interno delle celle, la macchina
sarebbe inutile, consisterebbe di milioni di piccoli sistemi non interagenti,
ognuno consistente di 16 bits.
Il kicking invece rende la CAM 8 molto flessibile per lo studio e lo
sviluppo di esperimenti con automi cellulari.
Il kicking è applicato a un intero spazio (attenzione: si sta
considerando il kick, non lo scan.), non è possibile limitarlo ad una
parte, per ovvi problemi di memoria che questo comporterebbe.
I bits in un layer possono essere rigidamente traslati in ogni direzione
o combinazioni di direzioni, fino alla massima proporzione che è uguale
alla grandezza di ogni dimensione impostata nel comando ... BY ... SECTOR.
Il kick è compiuto specificando il comando kick, seguito dai suoi
parametri per ogni layer:
kick nord field 1 x 2 y
sud field -1 x 3 y
significa che il layer nord viene spostato di 1 lungo la direzione x e 2 lungo
la direzione y, mentre il layer sud viene spostato di -1 lungo x e 3 lungo y.
kick 16-layers: 1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 x
16-layers: 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 y
e ha lo stesso effetto di prima, ogni posizione corrisponde a un layer.
Le parole x e y si riferiscono alle dimensioni specificate tramite il comando
... BY ... SECTOR.3 kn.32 by 32 by 64 by 64 sector kick 0 layer 4 x 4 y 2 z 1 3 knIl kick è uno strumento potente e vedremo negli esempi come sfruttarlo.
La CAM 8 è in grado di eseguire complesse e composte regole di update,
per simulare comportamenti dinamici complicati.
La lookup table è memorizzata in SRAM per poter essere più veloce
possibile e ogni suo indirizzo corrisponde a un possibile stato di una cella.
Allo stato di una cella, allora, posso associare tramite la LUT un nuovo
valore.
Le lut sono costruite seguendo le regole dell'automa; possiamo pensare di
crearle manualmente, oppure, più agilmente, affidarci alle parole del
CamForth create per questi scopi.
Vediamo un esempio.
Prima di tutto bisogna definire una nuova parola Forth; ne costruisco una che
segua le regole HPP:
0 0 == nord 1 1 == sud 2 2 == est 3 3 == ovest : hpp-rule nord sud = \ confronto nord e sud est ovest = \ e est con ovest and \ se sono vere entrambe... if est <-> nord \ ...scambio ovest <-> sud then ; \ altrimenti non cambio nientehpp-rule ora è definita nel dizionario e può essere usata per costruire la regola.
nord sud = è
un'operazione su bits, cioè il bit del campo nord di una singola cella con
il bit del campo sud sempre della stessa cella. <-> è ora semplice da spiegare:
est<->nord semplicemente scambia i contenuti dei bit est e nord. nord sud xor -> ovest compie un xor logico tra i campi
nord e sud e il risultato lo inserisce nel campo ovest. rule>table .
Prima di eseguire rule>table però, dobbiamo avere un buffer
sufficientemente grande da contenere la regola che vogliamo esplicitare, in
questo modo: create-table nometabella
16 create-buffer hpp-table rule>table hpp-rule hpp-tablehpp-table è pronta per essere trasmessa alla lut inattiva.
lut-data hpp-table \ la LUT inattiva
\ viene scritta dal buffer hpp-table
Le lut sono due e una sola alla volta può essere attiva, quella non attiva
è disponibile per essere caricata con una nuova regola. switch-luts.
Usualmente durante un esperimento questa parola CAMForth è utilizzata
dopo aver caricato una nuova regola nella lut inattiva, rendendola quindi
attiva e subito disponibile per compiere un update delle celle.
La lut resa inattiva è disponibile per essere caricata con una ulteriore
regola che si rendesse necessaria allo svolgimento dell'esperimento.
Per ricavare più che un giudizio qualitativo sui risultati
dell'esperimento, il programmatore della CAM 8 deve avere degli strumenti
per compiere misurazioni sullo spazio delle celle.
Nella CAM 8 il meccanismo di conteggio degli eventi rende possibile eseguire
conteggi dei risultati di una specificata funzione booleana a due ingressi
compiuta sul flusso dei bits delle celle e su un altro flusso di dati
selezionato dall'utente.
Questo meccanismo consiste, per ogni layer di ogni modulo, di un registro
interno Event Counter non accessibile direttamente dall'utente,
da un registro Event Source specificante cosa debba essere contato
e dal registro Event Count Buffer dove può essere trasferito il
contenuto dell'Event Conter e quindi letto dall'utente.
Per compiere il trasferimento dal registro Event Counter all'Event Count Buffer
occorre impostare a 1 il bit Event Count Transfer del registro
Run Mode.
Quindi è il solo Event Count Buffer ad essere letto: Event Count viene
incrementato ogni volta che si verifica l'evento scelto tramite Event Source.
Importante è sapere che il contenuto nell'Event Counter viene azzerato
ogni volta che si compie una lettura tramite Event Count Buffer.
Inoltre, scrivendo un valore in Event Count Buffer, questo sovrascriverà
il registro Event Counter.
In questa maniera si può azzerare, leggere o impostare il contatore di
eventi.
Vediamo ora precisamente cosa può essere contato.
La funzione booleana a due ingressi può essere scelta tra tutte le
possibili funzioni, mentre per gli ingressi abbiamo una situazione più
rigida: un ingresso sono i siti, l'altro può venir scelto tra bit
unglued, host, flywheel, lut.
Il sottoregistro Event Source.Select sceglie tra questi quattro ingressi,
mentre la parte restante Event Source.Map sceglie la funzione booleana da
eseguire.
Event Source è gestito da comandi CAMForth, quindi non si dovrà
intervenire direttamente.
Ad esempio per impostare il conteggio degli 0 contenuti in uscita dalla LUT,
(si badi bene uscita) cioè i rimpiazzi ai vecchi siti, si dovrà
scrivere:
event-src lut map not map!
oppure per fare un AND tra siti e dati host:
event-src site map host map and map!
Una volta terminato lo scan, per leggere il risultato del conteggio possiamo
dare il comando
run new-count
che trasferisce il contenuto di Event Counter in Event Count Buffer e poi
fa partire un nuovo scan.
Se non vogliamo un nuovo scan, ma vogliamo solo leggere il conteggio o
azzerarlo:
run no-scan new-count
Ora troviamo nell'Event Count Buffer un risultato che possiamo salvare
scrivendolo in una predefinita variabile Forth:
event read mia-variabile
il quale riempe mia-variabile con il conteggio di tutti i 16 layers, in ordine
broadside, come in figura 2.

La grandezza di questa variabile dovrà essere esattamente uguale a
log2( N ) + 1, dove N è il numero di siti aggiornati tramite lo scan
eseguito.
Una dimensione diversa porta a un errore.
Esempio:
new-experiment \ inizializzazione
8 by 8 sector \ bidimensionale
isolate-sectors \ moduli isolati senza uso del glue
step \ trasmetto la steplist
64 create-buffer inizio \ creo una variabile dove memorizzare
\ il pattern iniziale
inizio \ buffer inizio
b# \ imposto il modo binario
1011100011010010100100100100100010111110000101010110001010110011
reg! \ memorizzo in inizio il valore sopra
7 create-buffer conteggio \ preparo la variabile dove
\ inserire il conteggio
site-src host \ nei siti vengono scritti
\ i bits provenienti dall'host
scan-io inizio \ nel registro scan-io viene
\ scritto il pattern
site-src site \ ora faccio in modo che uno scan
\ non modifichi i siti
run no-scan new-count \ azzero il conteggio
event-src site \ conto il numero di 1 nei siti
run free \ faccio partire uno scan
run no-scan new-count \ leggo il conteggio e azzero
event read conteggio \ ora il conteggio e' disponibile
\ nella variabile conteggio
step \ trasmetto la steplist
La CAM 8 può ovviamente utilizzare un monitor VGA esterno.
Questo è un modo molto diretto per mostrare i risultati.
Un altro modo per conoscere le elaborazioni è ricevere i risultati
sull'host tramite buffers per mezzo dello stesso registro utilizzato per
inizializzare lo spazio: il registro Scan I/O.
Per scegliere cosa visualizzare sul video bisogna usare il registro
Display Source, con un meccanismo molto simile al registro Event Source.
Il sottoregistro Display Source.Select sceglie tra: site, host, flywheel, lut.
Il sottoregistro Display Source.Map combina eventualmente i bits dei
siti con uno degli ingressi sopra, con una funzione booleana a due ingressi.
Il comando tipico per la visualizzazione dei siti è display site.
L'utilizzatore raramente dovrà occuparsi direttamente di quello che succede
nei registri, poiché con i normali comandi CAMForth visti finora possiamo
compiere ogni tipo di esperimento.
Certamente esisteranno dei casi o particolari richieste che i comandi standard
non potranno compiere, essendo pensati per un funzionamento normale.
Ma la CAM 8 è molto flessibile e manipolando direttamente i registri si
possono compiere le operazioni più disparate.
Le parole usate finora fanno uso delle istruzioni CAM 8.
Queste consistono di quattro campi a 32 bits:
: ; con la particolarità di essere più agile nel cambiare i
comportamenti di default di carico di registri e di sottoregistri, rendendo
l'esecuzione dell'esperimento più rapida.
Al suo interno possono trovare posto tutti i comandi CAMForth, escluso
ovviamente le definizioni delle regole.