working with memory
This commit is contained in:
@@ -0,0 +1,929 @@
|
|||||||
|
# FPGA-Neural — Analisi datapath e benchmark ECP5
|
||||||
|
|
||||||
|
## 1. Obiettivo
|
||||||
|
|
||||||
|
Questa fase del progetto FPGA-Neural ha avuto lo scopo di verificare il comportamento sintetizzabile e le prestazioni del core neurale parametrico sul dispositivo:
|
||||||
|
|
||||||
|
**Lattice LFE5U-45F-8BG381C**
|
||||||
|
|
||||||
|
Configurazione FPGA:
|
||||||
|
|
||||||
|
- ECP5-45F
|
||||||
|
- Speed grade: `-8`
|
||||||
|
- Package: `CABGA381`
|
||||||
|
- 72 blocchi `MULT18X18D`
|
||||||
|
- circa 43.8k LUT/FF equivalenti
|
||||||
|
|
||||||
|
La configurazione funzionale utilizzata nei benchmark è:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DATA_WIDTH = 8 bit
|
||||||
|
ACC_WIDTH = 32 bit
|
||||||
|
N_INPUTS = 256
|
||||||
|
N_NEURONS = 4
|
||||||
|
PARALLEL = variabile
|
||||||
|
|
||||||
|
Il datapath implementa:
|
||||||
|
|
||||||
|
INT8 × INT8
|
||||||
|
↓
|
||||||
|
INT16
|
||||||
|
↓
|
||||||
|
sign extension
|
||||||
|
↓
|
||||||
|
INT32
|
||||||
|
↓
|
||||||
|
accumulation
|
||||||
|
↓
|
||||||
|
+ bias
|
||||||
|
↓
|
||||||
|
ReLU
|
||||||
|
↓
|
||||||
|
INT8 saturation
|
||||||
|
|
||||||
|
Lo scopo principale del benchmark è stato determinare il compromesso tra:
|
||||||
|
|
||||||
|
numero di MAC paralleli;
|
||||||
|
utilizzo dei DSP;
|
||||||
|
complessità del datapath;
|
||||||
|
routing;
|
||||||
|
frequenza massima;
|
||||||
|
latenza di elaborazione.
|
||||||
|
2. Architettura RTL
|
||||||
|
|
||||||
|
L'attuale datapath è organizzato gerarchicamente:
|
||||||
|
|
||||||
|
layer
|
||||||
|
│
|
||||||
|
┌────────┴────────┐
|
||||||
|
│ │
|
||||||
|
neuron 0 neuron N
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
neuron_parallel neuron_parallel
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
mac8 mac8
|
||||||
|
│ │
|
||||||
|
MAC × PARALLEL MAC × PARALLEL
|
||||||
|
|
||||||
|
Ogni neurone elabora N_INPUTS ingressi a gruppi di PARALLEL.
|
||||||
|
|
||||||
|
Con:
|
||||||
|
|
||||||
|
N_INPUTS = 256
|
||||||
|
|
||||||
|
il numero di gruppi è:
|
||||||
|
|
||||||
|
GROUPS = 256 / PARALLEL
|
||||||
|
|
||||||
|
Pertanto:
|
||||||
|
|
||||||
|
PARALLEL Gruppi per neurone
|
||||||
|
16 16
|
||||||
|
8 32
|
||||||
|
4 64
|
||||||
|
2 128
|
||||||
|
|
||||||
|
I quattro neuroni vengono elaborati contemporaneamente.
|
||||||
|
|
||||||
|
3. MAC unit
|
||||||
|
|
||||||
|
Il modulo mac_unit implementa un singolo prodotto-accumulatore.
|
||||||
|
|
||||||
|
Per la configurazione INT8:
|
||||||
|
|
||||||
|
x : signed INT8
|
||||||
|
w : signed INT8
|
||||||
|
|
||||||
|
Il prodotto è:
|
||||||
|
|
||||||
|
INT8 × INT8 = INT16
|
||||||
|
|
||||||
|
Il risultato viene quindi esteso con segno a 32 bit:
|
||||||
|
|
||||||
|
INT16 → INT32
|
||||||
|
|
||||||
|
e sommato all'accumulatore.
|
||||||
|
|
||||||
|
L'operazione fondamentale è quindi:
|
||||||
|
|
||||||
|
acc_out = acc_in + (x × w)
|
||||||
|
|
||||||
|
L'implementazione è completamente parametrica rispetto a:
|
||||||
|
|
||||||
|
DATA_WIDTH
|
||||||
|
ACC_WIDTH
|
||||||
|
4. Balanced adder tree
|
||||||
|
|
||||||
|
Una modifica importante rispetto alla prima implementazione è stata la sostituzione dell'accumulatore combinazionale lineare con un balanced binary adder tree.
|
||||||
|
|
||||||
|
Una riduzione lineare avrebbe prodotto:
|
||||||
|
|
||||||
|
((((p0 + p1) + p2) + p3) + ...)
|
||||||
|
|
||||||
|
con profondità:
|
||||||
|
|
||||||
|
O(PARALLEL)
|
||||||
|
|
||||||
|
La nuova implementazione utilizza invece:
|
||||||
|
|
||||||
|
sum
|
||||||
|
/ \
|
||||||
|
sum sum
|
||||||
|
/ \ / \
|
||||||
|
p0 p1 p2 p3
|
||||||
|
|
||||||
|
La profondità diventa:
|
||||||
|
|
||||||
|
O(log2(PARALLEL))
|
||||||
|
|
||||||
|
Per esempio:
|
||||||
|
|
||||||
|
PARALLEL = 8
|
||||||
|
→ 3 livelli
|
||||||
|
|
||||||
|
PARALLEL = 16
|
||||||
|
→ 4 livelli
|
||||||
|
|
||||||
|
PARALLEL = 32
|
||||||
|
→ 5 livelli
|
||||||
|
|
||||||
|
Questa modifica riduce significativamente la profondità combinazionale del datapath.
|
||||||
|
|
||||||
|
5. neuron_parallel
|
||||||
|
|
||||||
|
neuron_parallel esegue il calcolo di un singolo neurone.
|
||||||
|
|
||||||
|
Il funzionamento è:
|
||||||
|
|
||||||
|
start
|
||||||
|
↓
|
||||||
|
group 0
|
||||||
|
↓
|
||||||
|
group 1
|
||||||
|
↓
|
||||||
|
...
|
||||||
|
↓
|
||||||
|
group N
|
||||||
|
↓
|
||||||
|
+ bias
|
||||||
|
↓
|
||||||
|
ReLU
|
||||||
|
↓
|
||||||
|
saturation
|
||||||
|
↓
|
||||||
|
done
|
||||||
|
|
||||||
|
Durante ogni ciclo viene elaborato un gruppo di:
|
||||||
|
|
||||||
|
PARALLEL
|
||||||
|
|
||||||
|
prodotti.
|
||||||
|
|
||||||
|
L'accumulatore mantiene il risultato tra un gruppo e il successivo.
|
||||||
|
|
||||||
|
6. Test funzionale
|
||||||
|
|
||||||
|
Il testbench sim/parametric_tb.v utilizza:
|
||||||
|
|
||||||
|
DATA_WIDTH = 8
|
||||||
|
N_INPUTS = 256
|
||||||
|
N_NEURONS = 4
|
||||||
|
PARALLEL = variabile
|
||||||
|
ACC_WIDTH = 32
|
||||||
|
|
||||||
|
Sono stati definiti quattro casi di test.
|
||||||
|
|
||||||
|
N0 — accumulazione su più gruppi
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
Pesi:
|
||||||
|
|
||||||
|
primi 32 = +1
|
||||||
|
restanti = 0
|
||||||
|
|
||||||
|
Risultato:
|
||||||
|
|
||||||
|
32 × 1 × 1 = 32
|
||||||
|
|
||||||
|
Output atteso:
|
||||||
|
|
||||||
|
32
|
||||||
|
|
||||||
|
Questo test verifica soprattutto la corretta gestione dell'accumulatore attraverso più gruppi.
|
||||||
|
|
||||||
|
N1 — bias
|
||||||
|
|
||||||
|
Pesi:
|
||||||
|
|
||||||
|
tutti = 0
|
||||||
|
|
||||||
|
Bias:
|
||||||
|
|
||||||
|
+10
|
||||||
|
|
||||||
|
Output atteso:
|
||||||
|
|
||||||
|
10
|
||||||
|
N2 — ReLU
|
||||||
|
|
||||||
|
Pesi:
|
||||||
|
|
||||||
|
tutti = -1
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
tutti = +1
|
||||||
|
|
||||||
|
Il risultato è negativo.
|
||||||
|
|
||||||
|
La ReLU produce:
|
||||||
|
|
||||||
|
0
|
||||||
|
N3 — saturazione
|
||||||
|
|
||||||
|
Pesi:
|
||||||
|
|
||||||
|
primi 32 = +4
|
||||||
|
restanti = 0
|
||||||
|
|
||||||
|
Input:
|
||||||
|
|
||||||
|
tutti = +1
|
||||||
|
|
||||||
|
Risultato:
|
||||||
|
|
||||||
|
32 × 4 = 128
|
||||||
|
|
||||||
|
L'uscita INT8 positiva viene saturata:
|
||||||
|
|
||||||
|
128 → 127
|
||||||
|
7. Risultato simulazione
|
||||||
|
|
||||||
|
Il test è passato con PARALLEL=16:
|
||||||
|
|
||||||
|
PARALLEL = 16
|
||||||
|
|
||||||
|
PASS N0: 32
|
||||||
|
PASS N1: 10
|
||||||
|
PASS N2: 0 (ReLU)
|
||||||
|
PASS N3: 127 (saturation)
|
||||||
|
|
||||||
|
INT8 PARAMETRIC TEST PASSED
|
||||||
|
|
||||||
|
È passato anche con PARALLEL=32:
|
||||||
|
|
||||||
|
PARALLEL = 32
|
||||||
|
|
||||||
|
PASS N0: 32
|
||||||
|
PASS N1: 10
|
||||||
|
PASS N2: 0 (ReLU)
|
||||||
|
PASS N3: 127 (saturation)
|
||||||
|
|
||||||
|
INT8 PARAMETRIC TEST PASSED
|
||||||
|
|
||||||
|
La correttezza funzionale del datapath parametrico è quindi confermata.
|
||||||
|
|
||||||
|
8. Sintesi e Place & Route
|
||||||
|
|
||||||
|
Dopo la simulazione il datapath è stato sintetizzato per ECP5 utilizzando:
|
||||||
|
|
||||||
|
Yosys
|
||||||
|
|
||||||
|
e successivamente piazzato e instradato con:
|
||||||
|
|
||||||
|
nextpnr-ecp5
|
||||||
|
|
||||||
|
Target:
|
||||||
|
|
||||||
|
LFE5U-45F
|
||||||
|
CABGA381
|
||||||
|
Speed grade -8
|
||||||
|
|
||||||
|
Il wrapper di benchmark genera internamente:
|
||||||
|
|
||||||
|
input;
|
||||||
|
pesi;
|
||||||
|
bias;
|
||||||
|
segnali di test.
|
||||||
|
|
||||||
|
In questo modo non vengono portati all'esterno i giganteschi bus del modello neurale.
|
||||||
|
|
||||||
|
Il top-level espone solamente:
|
||||||
|
|
||||||
|
clk
|
||||||
|
rst
|
||||||
|
start
|
||||||
|
y_bus
|
||||||
|
busy
|
||||||
|
done
|
||||||
|
|
||||||
|
Questa modifica è stata fondamentale.
|
||||||
|
|
||||||
|
Il primo tentativo esponeva infatti direttamente:
|
||||||
|
|
||||||
|
x_bus ≈ 2048 bit
|
||||||
|
weights ≈ 8192 bit
|
||||||
|
bias ≈ 32 bit
|
||||||
|
|
||||||
|
portando a oltre 10.000 I/O fisiche richieste.
|
||||||
|
|
||||||
|
Il risultato era:
|
||||||
|
|
||||||
|
TRELLIS_IO: 10309/245
|
||||||
|
|
||||||
|
e quindi un errore di placement.
|
||||||
|
|
||||||
|
Il problema non era la dimensione logica del circuito, ma esclusivamente il numero di I/O.
|
||||||
|
|
||||||
|
9. PARALLEL = 16
|
||||||
|
|
||||||
|
Risorse sintetizzate:
|
||||||
|
|
||||||
|
LUT4 ≈ 2531
|
||||||
|
DFF = 186
|
||||||
|
|
||||||
|
Risorse FPGA:
|
||||||
|
|
||||||
|
MULT18X18D = 64 / 72
|
||||||
|
|
||||||
|
quindi:
|
||||||
|
|
||||||
|
88% dei DSP
|
||||||
|
|
||||||
|
Timing:
|
||||||
|
|
||||||
|
Fmax ≈ 52.13 MHz
|
||||||
|
Tcrit ≈ 19.18 ns
|
||||||
|
|
||||||
|
Il percorso critico risultava fortemente influenzato dal routing.
|
||||||
|
|
||||||
|
Configurazione:
|
||||||
|
|
||||||
|
PARALLEL = 16
|
||||||
|
N_NEURONS = 4
|
||||||
|
|
||||||
|
produce:
|
||||||
|
|
||||||
|
16 × 4 = 64 MAC simultanei
|
||||||
|
10. PARALLEL = 8
|
||||||
|
|
||||||
|
Risorse:
|
||||||
|
|
||||||
|
MULT18X18D = 32 / 72
|
||||||
|
|
||||||
|
quindi:
|
||||||
|
|
||||||
|
32 DSP
|
||||||
|
|
||||||
|
Con quattro neuroni:
|
||||||
|
|
||||||
|
8 × 4 = 32 MAC simultanei
|
||||||
|
|
||||||
|
Timing:
|
||||||
|
|
||||||
|
Fmax ≈ 61.71 MHz
|
||||||
|
Tcrit ≈ 16.20 ns
|
||||||
|
|
||||||
|
Composizione del percorso critico:
|
||||||
|
|
||||||
|
logic ≈ 6.43 ns
|
||||||
|
routing ≈ 9.77 ns
|
||||||
|
total ≈ 16.20 ns
|
||||||
|
|
||||||
|
Questa configurazione è particolarmente importante perché corrisponde esattamente al target originale di:
|
||||||
|
|
||||||
|
32 MAC hardware simultanei
|
||||||
|
11. PARALLEL = 4
|
||||||
|
|
||||||
|
Risorse:
|
||||||
|
|
||||||
|
LUT4 = 804
|
||||||
|
DFF = 194
|
||||||
|
|
||||||
|
MULT18X18D = 16 / 72
|
||||||
|
|
||||||
|
Quindi:
|
||||||
|
|
||||||
|
16 MAC simultanei
|
||||||
|
|
||||||
|
Timing:
|
||||||
|
|
||||||
|
Fmax ≈ 75.01 MHz
|
||||||
|
Tcrit ≈ 13.33 ns
|
||||||
|
|
||||||
|
Composizione:
|
||||||
|
|
||||||
|
logic ≈ 6.67 ns
|
||||||
|
routing ≈ 6.66 ns
|
||||||
|
total ≈ 13.33 ns
|
||||||
|
|
||||||
|
In questa configurazione logica e routing sono quasi perfettamente bilanciati.
|
||||||
|
|
||||||
|
12. PARALLEL = 2
|
||||||
|
|
||||||
|
Risorse:
|
||||||
|
|
||||||
|
LUT4 = 481
|
||||||
|
DFF = 198
|
||||||
|
|
||||||
|
MULT18X18D = 8 / 72
|
||||||
|
|
||||||
|
Quindi:
|
||||||
|
|
||||||
|
8 MAC simultanei
|
||||||
|
|
||||||
|
Timing finale dopo routing:
|
||||||
|
|
||||||
|
Fmax ≈ 87.88 MHz
|
||||||
|
Tcrit ≈ 11.38 ns
|
||||||
|
|
||||||
|
Composizione:
|
||||||
|
|
||||||
|
logic ≈ 6.43 ns
|
||||||
|
routing ≈ 4.95 ns
|
||||||
|
total ≈ 11.38 ns
|
||||||
|
|
||||||
|
Il design è stato quindi verificato con target:
|
||||||
|
|
||||||
|
80 MHz
|
||||||
|
|
||||||
|
ottenendo:
|
||||||
|
|
||||||
|
87.88 MHz
|
||||||
|
|
||||||
|
e:
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
Il margine teorico rispetto a 80 MHz è:
|
||||||
|
|
||||||
|
T80MHz = 12.50 ns
|
||||||
|
|
||||||
|
12.50 - 11.38 ≈ 1.12 ns
|
||||||
|
13. Tabella comparativa
|
||||||
|
PARALLEL MAC/neurone Neuroni MAC totali DSP Fmax Tcrit 80 MHz
|
||||||
|
16 16 4 64 64 52.13 MHz 19.18 ns FAIL
|
||||||
|
8 8 4 32 32 61.71 MHz 16.20 ns FAIL
|
||||||
|
4 4 4 16 16 75.01 MHz 13.33 ns FAIL
|
||||||
|
2 2 4 8 8 87.88 MHz 11.38 ns PASS
|
||||||
|
14. Interpretazione
|
||||||
|
|
||||||
|
I risultati mostrano chiaramente il trade-off fondamentale.
|
||||||
|
|
||||||
|
Riducendo PARALLEL:
|
||||||
|
|
||||||
|
PARALLEL ↓
|
||||||
|
↓
|
||||||
|
MAC simultanei ↓
|
||||||
|
↓
|
||||||
|
DSP ↓
|
||||||
|
↓
|
||||||
|
adder tree ↓
|
||||||
|
↓
|
||||||
|
routing/congestione ↓
|
||||||
|
↓
|
||||||
|
Fmax ↑
|
||||||
|
|
||||||
|
ma contemporaneamente:
|
||||||
|
|
||||||
|
PARALLEL ↓
|
||||||
|
↓
|
||||||
|
numero gruppi ↑
|
||||||
|
↓
|
||||||
|
cicli necessari ↑
|
||||||
|
↓
|
||||||
|
latenza ↑
|
||||||
|
|
||||||
|
Quindi la frequenza massima non è sufficiente per scegliere la configurazione.
|
||||||
|
|
||||||
|
Occorre considerare il throughput complessivo:
|
||||||
|
|
||||||
|
throughput ≈ MAC_per_cycle × clock_frequency
|
||||||
|
|
||||||
|
A parità di quattro neuroni:
|
||||||
|
|
||||||
|
P2
|
||||||
|
8 MAC × 87.88 MHz
|
||||||
|
≈ 703 M MAC/s
|
||||||
|
P4
|
||||||
|
16 MAC × 75.01 MHz
|
||||||
|
≈ 1.20 G MAC/s
|
||||||
|
P8
|
||||||
|
32 MAC × 61.71 MHz
|
||||||
|
≈ 1.97 G MAC/s
|
||||||
|
P16
|
||||||
|
64 MAC × 52.13 MHz
|
||||||
|
≈ 3.34 G MAC/s
|
||||||
|
|
||||||
|
Questi valori sono una misura teorica del throughput del datapath MAC, non ancora del throughput end-to-end della rete, perché non includono i limiti della memoria esterna, del trasferimento dei pesi e del controller.
|
||||||
|
|
||||||
|
15. Scelta architetturale
|
||||||
|
|
||||||
|
Il risultato più importante del benchmark è che PARALLEL=8 rimane il candidato naturale per l'architettura V1 se l'obiettivo iniziale di progetto è mantenere circa:
|
||||||
|
|
||||||
|
32 MAC simultanei
|
||||||
|
|
||||||
|
Infatti:
|
||||||
|
|
||||||
|
PARALLEL = 8
|
||||||
|
N_NEURONS = 4
|
||||||
|
|
||||||
|
→ 32 MAC
|
||||||
|
→ 32 DSP / 72
|
||||||
|
→ 61.71 MHz
|
||||||
|
|
||||||
|
L'utilizzo DSP è ancora relativamente basso:
|
||||||
|
|
||||||
|
44% circa
|
||||||
|
|
||||||
|
lasciando risorse per:
|
||||||
|
|
||||||
|
controller memoria;
|
||||||
|
buffer;
|
||||||
|
interfaccia SPI;
|
||||||
|
DMA;
|
||||||
|
gestione layer;
|
||||||
|
eventuali pipeline;
|
||||||
|
future funzioni di controllo.
|
||||||
|
|
||||||
|
PARALLEL=2 è invece la configurazione più semplice da temporizzare tra quelle testate:
|
||||||
|
|
||||||
|
87.88 MHz
|
||||||
|
|
||||||
|
e passa il target di 80 MHz.
|
||||||
|
|
||||||
|
Tuttavia richiede:
|
||||||
|
|
||||||
|
128 gruppi
|
||||||
|
|
||||||
|
per elaborare un neurone da 256 ingressi.
|
||||||
|
|
||||||
|
Per questo motivo non è opportuno adottarlo automaticamente come configurazione definitiva solo perché raggiunge la frequenza più elevata.
|
||||||
|
|
||||||
|
16. Timing a 100 MHz
|
||||||
|
|
||||||
|
Il target di:
|
||||||
|
|
||||||
|
100 MHz
|
||||||
|
|
||||||
|
non viene attualmente raggiunto.
|
||||||
|
|
||||||
|
Il miglior risultato è:
|
||||||
|
|
||||||
|
87.88 MHz
|
||||||
|
|
||||||
|
con PARALLEL=2.
|
||||||
|
|
||||||
|
Il percorso critico è ancora:
|
||||||
|
|
||||||
|
weight FF
|
||||||
|
↓
|
||||||
|
MULT18X18D
|
||||||
|
↓
|
||||||
|
products
|
||||||
|
↓
|
||||||
|
adder/carry chain
|
||||||
|
↓
|
||||||
|
acc_next
|
||||||
|
↓
|
||||||
|
ReLU / saturation
|
||||||
|
↓
|
||||||
|
output FF
|
||||||
|
|
||||||
|
Il problema non è quindi un'elevata occupazione delle risorse FPGA.
|
||||||
|
|
||||||
|
Al contrario, con P2 l'FPGA è utilizzato molto poco:
|
||||||
|
|
||||||
|
DSP ≈ 11%
|
||||||
|
LUT ≈ 1%
|
||||||
|
FF ≈ 0%
|
||||||
|
|
||||||
|
Il limite è principalmente temporale e dipende dal datapath combinazionale.
|
||||||
|
|
||||||
|
Per superare 100 MHz sarà probabilmente necessario introdurre una o più pipeline interne.
|
||||||
|
|
||||||
|
Questa ottimizzazione non è però ancora necessaria per procedere con la prossima fase architetturale.
|
||||||
|
|
||||||
|
17. Vincoli LPF
|
||||||
|
|
||||||
|
Durante questi benchmark il file:
|
||||||
|
|
||||||
|
synth/ecp5/top.lpf
|
||||||
|
|
||||||
|
è stato lasciato senza vincoli di pin.
|
||||||
|
|
||||||
|
nextpnr viene eseguito con:
|
||||||
|
|
||||||
|
--lpf-allow-unconstrained
|
||||||
|
|
||||||
|
Pertanto i numerosi warning relativi a I/O non vincolate sono intenzionali.
|
||||||
|
|
||||||
|
Il benchmark verifica quindi:
|
||||||
|
|
||||||
|
sintesi
|
||||||
|
placement
|
||||||
|
routing
|
||||||
|
timing
|
||||||
|
|
||||||
|
e non:
|
||||||
|
|
||||||
|
pin assignment
|
||||||
|
I/O standard della scheda
|
||||||
|
signal integrity
|
||||||
|
|
||||||
|
I vincoli LPF reali verranno aggiunti quando sarà definito il pinout della scheda FPGA-Neural.
|
||||||
|
|
||||||
|
18. Decisione per la prossima fase
|
||||||
|
|
||||||
|
Non è utile proseguire con benchmark PARALLEL=1.
|
||||||
|
|
||||||
|
Il punto di interesse architetturale è già stato individuato.
|
||||||
|
|
||||||
|
La prossima fase deve spostare il progetto dal benchmark sintetico verso l'architettura reale:
|
||||||
|
|
||||||
|
HOST
|
||||||
|
│
|
||||||
|
│ SPI
|
||||||
|
▼
|
||||||
|
FPGA interface
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Memory interface
|
||||||
|
│
|
||||||
|
┌───────────┴───────────┐
|
||||||
|
▼ ▼
|
||||||
|
PSRAM 8 MB FPGA BRAM
|
||||||
|
│ │
|
||||||
|
└──────────┬────────────┘
|
||||||
|
▼
|
||||||
|
tile/buffer
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
MAC engine
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
accumulator
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
activation
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
output
|
||||||
|
|
||||||
|
Il core layer e neuron_parallel dovrà quindi essere separato dalla memoria fisica attraverso una Memory Interface.
|
||||||
|
|
||||||
|
19. Memoria V1
|
||||||
|
|
||||||
|
La memoria di lavoro prevista è:
|
||||||
|
|
||||||
|
ISSI IS66WVE4M16EBLL-70BLI
|
||||||
|
|
||||||
|
Caratteristiche:
|
||||||
|
|
||||||
|
64 Mbit
|
||||||
|
8 MB
|
||||||
|
4M × 16
|
||||||
|
parallel PSRAM
|
||||||
|
asynchronous/page mode
|
||||||
|
70 ns
|
||||||
|
2.7–3.6 V
|
||||||
|
48-TFBGA
|
||||||
|
6 × 8 mm
|
||||||
|
|
||||||
|
La memoria volatile sarà utilizzata come working memory durante l'inferenza.
|
||||||
|
|
||||||
|
La memoria persistente prevista è:
|
||||||
|
|
||||||
|
Winbond W25Q128JVS
|
||||||
|
|
||||||
|
con:
|
||||||
|
|
||||||
|
128 Mbit
|
||||||
|
16 MB
|
||||||
|
SPI NOR Flash
|
||||||
|
|
||||||
|
La divisione dei ruoli è:
|
||||||
|
|
||||||
|
W25Q128JVS
|
||||||
|
↓
|
||||||
|
persistent storage
|
||||||
|
↓
|
||||||
|
weights
|
||||||
|
biases
|
||||||
|
network metadata
|
||||||
|
FPGA/network configuration
|
||||||
|
|
||||||
|
e:
|
||||||
|
|
||||||
|
IS66WVE4M16EBLL
|
||||||
|
↓
|
||||||
|
runtime working memory
|
||||||
|
↓
|
||||||
|
input/output tensors
|
||||||
|
intermediate data
|
||||||
|
weight tiles
|
||||||
|
|
||||||
|
Infine:
|
||||||
|
|
||||||
|
FPGA BRAM
|
||||||
|
↓
|
||||||
|
local working buffers
|
||||||
|
tiles
|
||||||
|
accumulators
|
||||||
|
20. Conclusioni
|
||||||
|
|
||||||
|
La fase di caratterizzazione del datapath ha prodotto i seguenti risultati:
|
||||||
|
|
||||||
|
Il datapath INT8/INT32 è funzionalmente corretto.
|
||||||
|
Il test parametrico è passato.
|
||||||
|
Il balanced adder tree ha sostituito con successo la precedente riduzione lineare.
|
||||||
|
Il design è sintetizzabile per LFE5U-45F.
|
||||||
|
Il placement e routing sono stati completati correttamente.
|
||||||
|
Il problema iniziale delle migliaia di I/O è stato eliminato spostando i test vector all'interno del wrapper.
|
||||||
|
PARALLEL=8 implementa esattamente 32 MAC simultanei con quattro neuroni.
|
||||||
|
PARALLEL=2 raggiunge 87.88 MHz e supera il target di 80 MHz.
|
||||||
|
Il limite attuale a 100 MHz è dovuto al percorso combinazionale, non alla saturazione delle risorse FPGA.
|
||||||
|
Non è necessario continuare il benchmark verso PARALLEL=1.
|
||||||
|
|
||||||
|
La baseline architetturale rimane quindi:
|
||||||
|
|
||||||
|
LFE5U-45F-8BG381C
|
||||||
|
INT8 / INT32
|
||||||
|
256 inputs
|
||||||
|
4 neurons
|
||||||
|
PARALLEL parametrico
|
||||||
|
|
||||||
|
con:
|
||||||
|
|
||||||
|
PARALLEL = 8
|
||||||
|
|
||||||
|
come candidato principale per la configurazione orientata al throughput e:
|
||||||
|
|
||||||
|
PARALLEL = 2
|
||||||
|
|
||||||
|
come riferimento per la configurazione orientata alla frequenza.
|
||||||
|
|
||||||
|
La prossima attività significativa è l'integrazione della Memory Interface con la PSRAM esterna, mantenendo PARALLEL come parametro del motore computazionale.
|
||||||
|
|
||||||
|
Appendice A — Toolchain
|
||||||
|
Yosys
|
||||||
|
|
||||||
|
Yosys è il tool di sintesi RTL.
|
||||||
|
|
||||||
|
Flusso:
|
||||||
|
|
||||||
|
Verilog RTL
|
||||||
|
↓
|
||||||
|
elaborazione
|
||||||
|
↓
|
||||||
|
ottimizzazione
|
||||||
|
↓
|
||||||
|
mapping ECP5
|
||||||
|
↓
|
||||||
|
JSON netlist
|
||||||
|
|
||||||
|
Versione utilizzata:
|
||||||
|
|
||||||
|
Yosys 0.68+post
|
||||||
|
|
||||||
|
Binary:
|
||||||
|
|
||||||
|
/opt/homebrew/bin/yosys
|
||||||
|
Project Trellis
|
||||||
|
|
||||||
|
Project Trellis fornisce il database open-source dell'architettura ECP5 e gli strumenti necessari all'implementazione.
|
||||||
|
|
||||||
|
Tra gli strumenti disponibili:
|
||||||
|
|
||||||
|
ecppack
|
||||||
|
ecppll
|
||||||
|
ecpbram
|
||||||
|
ecpunpack
|
||||||
|
|
||||||
|
Installazione utilizzata:
|
||||||
|
|
||||||
|
/tmp/prjtrellis/install
|
||||||
|
nextpnr-ecp5
|
||||||
|
|
||||||
|
nextpnr-ecp5 esegue:
|
||||||
|
|
||||||
|
placement
|
||||||
|
routing
|
||||||
|
timing analysis
|
||||||
|
|
||||||
|
Versione:
|
||||||
|
|
||||||
|
nextpnr-0.11.1-19-g8dbcee5
|
||||||
|
|
||||||
|
Binary:
|
||||||
|
|
||||||
|
/tmp/nextpnr/build/nextpnr-ecp5
|
||||||
|
|
||||||
|
Parametri principali:
|
||||||
|
|
||||||
|
--45k
|
||||||
|
|
||||||
|
seleziona LFE5U-45F.
|
||||||
|
|
||||||
|
--package CABGA381
|
||||||
|
|
||||||
|
seleziona il package.
|
||||||
|
|
||||||
|
--speed 8
|
||||||
|
|
||||||
|
seleziona speed grade -8.
|
||||||
|
|
||||||
|
--json
|
||||||
|
|
||||||
|
carica la netlist generata da Yosys.
|
||||||
|
|
||||||
|
--lpf
|
||||||
|
|
||||||
|
carica i vincoli di pin.
|
||||||
|
|
||||||
|
--lpf-allow-unconstrained
|
||||||
|
|
||||||
|
permette I/O non vincolate.
|
||||||
|
|
||||||
|
--freq 80
|
||||||
|
|
||||||
|
richiede un timing target di 80 MHz.
|
||||||
|
|
||||||
|
Icarus Verilog
|
||||||
|
|
||||||
|
Icarus Verilog viene utilizzato per la simulazione RTL.
|
||||||
|
|
||||||
|
Esempio:
|
||||||
|
|
||||||
|
iverilog -g2012 \
|
||||||
|
-Ptb.PARALLEL=16 \
|
||||||
|
-o sim/parametric_256x4_p16 \
|
||||||
|
sim/parametric_tb.v \
|
||||||
|
rtl/mac_unit.v \
|
||||||
|
rtl/mac8.v \
|
||||||
|
rtl/neuron_parallel.v \
|
||||||
|
rtl/layer.v
|
||||||
|
|
||||||
|
Esecuzione:
|
||||||
|
|
||||||
|
vvp sim/parametric_256x4_p16
|
||||||
|
|
||||||
|
Icarus verifica principalmente la correttezza funzionale del RTL.
|
||||||
|
|
||||||
|
Appendice B — Differenza tra simulazione e implementazione
|
||||||
|
Simulazione
|
||||||
|
Icarus Verilog
|
||||||
|
|
||||||
|
verifica:
|
||||||
|
|
||||||
|
algebra signed;
|
||||||
|
prodotti;
|
||||||
|
accumulazione;
|
||||||
|
gruppi;
|
||||||
|
bias;
|
||||||
|
ReLU;
|
||||||
|
saturazione;
|
||||||
|
segnali busy e done.
|
||||||
|
Implementazione
|
||||||
|
Yosys
|
||||||
|
+
|
||||||
|
nextpnr-ecp5
|
||||||
|
|
||||||
|
verifica:
|
||||||
|
|
||||||
|
sintetizzabilità;
|
||||||
|
mapping FPGA;
|
||||||
|
LUT;
|
||||||
|
FF;
|
||||||
|
DSP;
|
||||||
|
placement;
|
||||||
|
routing;
|
||||||
|
timing;
|
||||||
|
Fmax.
|
||||||
|
|
||||||
|
Le due verifiche sono complementari.
|
||||||
|
r
|
||||||
|
Appendice C — Stato attuale
|
||||||
|
RTL funzionale PASS
|
||||||
|
Simulazione parametrica PASS
|
||||||
|
Sintesi ECP5 PASS
|
||||||
|
Placement PASS
|
||||||
|
Routing PASS
|
||||||
|
|
||||||
|
PARALLEL=16 52.13 MHz
|
||||||
|
PARALLEL=8 61.71 MHz
|
||||||
|
PARALLEL=4 75.01 MHz
|
||||||
|
PARALLEL=2 87.88 MHz
|
||||||
|
|
||||||
|
Target 80 MHz, P2 PASS
|
||||||
|
Target 100 MHz FAIL
|
||||||
|
|
||||||
|
Memory Interface DA IMPLEMENTARE
|
||||||
|
PSRAM controller DA IMPLEMENTARE
|
||||||
|
Host SPI interface DA IMPLEMENTARE
|
||||||
|
Layer engine reale PROSSIMA FASE
|
||||||
@@ -0,0 +1,735 @@
|
|||||||
|
# FPGA Neural Network Engine
|
||||||
|
|
||||||
|
Hardware Neural Network Engine based on FPGA + dedicated RAM.
|
||||||
|
|
||||||
|
The project implements a **parametric hardware accelerator for neural networks**, designed to be reusable across different embedded systems and applications.
|
||||||
|
|
||||||
|
The fundamental design principle is that the neural-network computation is performed entirely inside the FPGA, while the host system communicates with the engine through a simple hardware-independent interface such as SPI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Project Goal
|
||||||
|
|
||||||
|
The goal of this project is to develop a reusable **Neural Network Engine implemented in FPGA hardware**.
|
||||||
|
|
||||||
|
The engine is composed of:
|
||||||
|
|
||||||
|
- FPGA;
|
||||||
|
- dedicated RAM connected to the FPGA;
|
||||||
|
- host interface, initially SPI and potentially Dual SPI.
|
||||||
|
|
||||||
|
The host system is not part of the neural-network computational datapath.
|
||||||
|
|
||||||
|
Possible host systems include:
|
||||||
|
|
||||||
|
- Linux SoCs;
|
||||||
|
- Raspberry-Pi-like systems;
|
||||||
|
- ESP32;
|
||||||
|
- microcontrollers;
|
||||||
|
- other embedded processors;
|
||||||
|
- development PCs.
|
||||||
|
|
||||||
|
The same Neural Network Engine architecture should therefore be usable in completely different systems.
|
||||||
|
|
||||||
|
```text
|
||||||
|
HOST SYSTEM
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ Linux / ESP32 / MCU │
|
||||||
|
│ │
|
||||||
|
│ Configuration │
|
||||||
|
│ Training │
|
||||||
|
│ Control │
|
||||||
|
└────────────┬────────────┘
|
||||||
|
│
|
||||||
|
SPI / Dual SPI
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ FPGA │
|
||||||
|
│ │
|
||||||
|
│ Neural Network Engine │
|
||||||
|
│ │
|
||||||
|
│ Compute / Control │
|
||||||
|
│ │
|
||||||
|
└────────────┬────────────┘
|
||||||
|
│
|
||||||
|
│
|
||||||
|
Dedicated RAM
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Architectural Principle
|
||||||
|
|
||||||
|
The FPGA is the actual neural-network accelerator.
|
||||||
|
|
||||||
|
The RAM required by the neural network is physically associated with the FPGA and is **not part of the host system memory**.
|
||||||
|
|
||||||
|
The host only provides:
|
||||||
|
|
||||||
|
- configuration;
|
||||||
|
- network parameters;
|
||||||
|
- input data;
|
||||||
|
- control;
|
||||||
|
- result retrieval.
|
||||||
|
|
||||||
|
The neural-network calculations themselves are executed by the FPGA.
|
||||||
|
|
||||||
|
This separation is a fundamental architectural requirement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Hardware Configuration vs Network Configuration
|
||||||
|
|
||||||
|
An important distinction is made between the **hardware architecture of the accelerator** and the **parameters of the neural network**.
|
||||||
|
|
||||||
|
## 3.1 FPGA hardware configuration
|
||||||
|
|
||||||
|
The physical architecture of the Neural Network Engine is defined when the FPGA design is synthesized and implemented.
|
||||||
|
|
||||||
|
Typical hardware parameters include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
N_INPUTS
|
||||||
|
N_NEURONS
|
||||||
|
N_LAYERS
|
||||||
|
PARALLEL
|
||||||
|
DATA_WIDTH
|
||||||
|
ACCUMULATOR_WIDTH
|
||||||
|
```
|
||||||
|
|
||||||
|
These parameters can therefore be Verilog/SystemVerilog parameters or equivalent synthesis-time configuration values.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
N_INPUTS = 32
|
||||||
|
N_NEURONS = 4
|
||||||
|
PARALLEL = 8
|
||||||
|
```
|
||||||
|
|
||||||
|
defines a specific hardware implementation optimized for that architecture.
|
||||||
|
|
||||||
|
The resulting FPGA bitstream contains the corresponding datapath.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3.2 Neural-network configuration
|
||||||
|
|
||||||
|
Once the FPGA has been configured and initialized, the actual neural-network parameters can be loaded through the host interface.
|
||||||
|
|
||||||
|
These parameters may include:
|
||||||
|
|
||||||
|
- weights;
|
||||||
|
- biases;
|
||||||
|
- activation parameters;
|
||||||
|
- quantization parameters;
|
||||||
|
- network-specific constants.
|
||||||
|
|
||||||
|
These values are stored in the RAM associated with the FPGA.
|
||||||
|
|
||||||
|
Therefore:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FPGA BITSTREAM
|
||||||
|
│
|
||||||
|
│ defines hardware architecture
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ Neural Network │
|
||||||
|
│ Hardware Engine │
|
||||||
|
└──────────┬──────────┘
|
||||||
|
│
|
||||||
|
│ loads
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ Dedicated RAM │
|
||||||
|
│ │
|
||||||
|
│ weights │
|
||||||
|
│ biases │
|
||||||
|
│ parameters │
|
||||||
|
│ buffers │
|
||||||
|
└─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
This provides an important separation between **hardware specialization** and **network data**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Application-Specific Neural Networks
|
||||||
|
|
||||||
|
The Neural Network Engine is not intended to implement one fixed neural network.
|
||||||
|
|
||||||
|
Instead, each application can define its own network.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Application A
|
||||||
|
16 inputs
|
||||||
|
8 neurons
|
||||||
|
1 output
|
||||||
|
|
||||||
|
Application B
|
||||||
|
32 inputs
|
||||||
|
16 neurons
|
||||||
|
4 outputs
|
||||||
|
|
||||||
|
Application C
|
||||||
|
64 inputs
|
||||||
|
multiple layers
|
||||||
|
custom parallelism
|
||||||
|
```
|
||||||
|
|
||||||
|
The FPGA hardware can then be generated specifically for the required architecture.
|
||||||
|
|
||||||
|
This allows the design to exploit the FPGA resources efficiently rather than implementing a completely generic and potentially inefficient neural-network processor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Parametric Compute Engine
|
||||||
|
|
||||||
|
The current implementation contains a parametric neural-network layer.
|
||||||
|
|
||||||
|
A validated configuration is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
N_INPUTS = 32
|
||||||
|
N_NEURONS = 4
|
||||||
|
PARALLEL = 8
|
||||||
|
```
|
||||||
|
|
||||||
|
The datapath processes the inputs in parallel groups.
|
||||||
|
|
||||||
|
Conceptually:
|
||||||
|
|
||||||
|
```text
|
||||||
|
32 inputs
|
||||||
|
│
|
||||||
|
├── 8 parallel MACs
|
||||||
|
│
|
||||||
|
├── 8 parallel MACs
|
||||||
|
│
|
||||||
|
├── 8 parallel MACs
|
||||||
|
│
|
||||||
|
└── 8 parallel MACs
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Accumulation
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Bias
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Activation
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Output
|
||||||
|
```
|
||||||
|
|
||||||
|
The architecture is intended to scale by changing the synthesis parameters.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Current Functional Validation
|
||||||
|
|
||||||
|
The `32 × 4 / PARALLEL = 8` configuration has been successfully simulated.
|
||||||
|
|
||||||
|
Test output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
========================================
|
||||||
|
PARAMETRIC LAYER TEST
|
||||||
|
N_INPUTS = 32
|
||||||
|
N_NEURONS = 4
|
||||||
|
PARALLEL = 8
|
||||||
|
========================================
|
||||||
|
|
||||||
|
PASS N0: 8192
|
||||||
|
PASS N1: 4096
|
||||||
|
PASS N2: 0 (ReLU)
|
||||||
|
PASS N3: 16384
|
||||||
|
|
||||||
|
========================================
|
||||||
|
PARAMETRIC TEST PASSED
|
||||||
|
========================================
|
||||||
|
```
|
||||||
|
|
||||||
|
Validated functionality:
|
||||||
|
|
||||||
|
- multiple inputs;
|
||||||
|
- multiple neurons;
|
||||||
|
- parallel MAC processing;
|
||||||
|
- accumulation across multiple input groups;
|
||||||
|
- bias handling;
|
||||||
|
- independent neuron outputs;
|
||||||
|
- ReLU activation;
|
||||||
|
- parametric layer architecture.
|
||||||
|
|
||||||
|
The corresponding test has been committed to the repository.
|
||||||
|
|
||||||
|
Commit:
|
||||||
|
|
||||||
|
```text
|
||||||
|
test: validate parametric 32x4 layer with parallelism 8
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. FPGA Boot and Initialization
|
||||||
|
|
||||||
|
The FPGA is configured during system initialization using its normal FPGA configuration mechanism.
|
||||||
|
|
||||||
|
The FPGA bitstream defines the hardware architecture of the Neural Network Engine.
|
||||||
|
|
||||||
|
Conceptually:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Power-on
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
FPGA configuration
|
||||||
|
│
|
||||||
|
│ bitstream
|
||||||
|
▼
|
||||||
|
Neural Network Engine available
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Host initialization
|
||||||
|
│
|
||||||
|
│ SPI
|
||||||
|
▼
|
||||||
|
Load network parameters
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Load weights / biases
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Engine ready
|
||||||
|
```
|
||||||
|
|
||||||
|
This means that the host does **not dynamically construct the FPGA datapath** during normal operation.
|
||||||
|
|
||||||
|
The datapath already exists in hardware.
|
||||||
|
|
||||||
|
The host configures the network data that the datapath operates on.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Host Interface
|
||||||
|
|
||||||
|
The primary external interface is intended to be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SPI
|
||||||
|
```
|
||||||
|
|
||||||
|
with possible future support for:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Dual SPI
|
||||||
|
```
|
||||||
|
|
||||||
|
The interface must remain independent of the host operating system.
|
||||||
|
|
||||||
|
The same hardware protocol should therefore be usable from:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Linux
|
||||||
|
ESP32
|
||||||
|
MCU
|
||||||
|
PC
|
||||||
|
```
|
||||||
|
|
||||||
|
The host interface should provide access to:
|
||||||
|
|
||||||
|
- control registers;
|
||||||
|
- status;
|
||||||
|
- network configuration;
|
||||||
|
- RAM;
|
||||||
|
- input data;
|
||||||
|
- output data;
|
||||||
|
- start/stop control;
|
||||||
|
- completion status.
|
||||||
|
|
||||||
|
A conceptual command sequence is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
RESET
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
CONFIGURE
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LOAD NETWORK PARAMETERS
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LOAD WEIGHTS
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LOAD BIASES
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LOAD INPUT
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
START
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
WAIT FOR DONE
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
READ OUTPUT
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Dedicated FPGA RAM
|
||||||
|
|
||||||
|
The RAM is considered part of the Neural Network Engine.
|
||||||
|
|
||||||
|
It is not intended to be supplied by the host system.
|
||||||
|
|
||||||
|
Depending on the final architecture, RAM may contain:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Weights
|
||||||
|
Biases
|
||||||
|
Input buffers
|
||||||
|
Intermediate layer buffers
|
||||||
|
Output buffers
|
||||||
|
Network parameters
|
||||||
|
```
|
||||||
|
|
||||||
|
The memory architecture must be designed according to:
|
||||||
|
|
||||||
|
- number of parallel MAC units;
|
||||||
|
- data width;
|
||||||
|
- required bandwidth;
|
||||||
|
- number of layers;
|
||||||
|
- buffering requirements;
|
||||||
|
- FPGA block-RAM resources;
|
||||||
|
- possible external RAM requirements.
|
||||||
|
|
||||||
|
The preferred architecture is that the FPGA directly controls this memory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Training
|
||||||
|
|
||||||
|
Training and inference are conceptually separated.
|
||||||
|
|
||||||
|
The first implementation does not require the FPGA to perform the complete training process.
|
||||||
|
|
||||||
|
Training can be performed externally:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PC / Linux / other host
|
||||||
|
│
|
||||||
|
│ training
|
||||||
|
▼
|
||||||
|
Network weights
|
||||||
|
│
|
||||||
|
│ SPI
|
||||||
|
▼
|
||||||
|
FPGA RAM
|
||||||
|
```
|
||||||
|
|
||||||
|
The FPGA then performs inference using the resulting parameters.
|
||||||
|
|
||||||
|
This approach greatly reduces the complexity of the initial hardware implementation.
|
||||||
|
|
||||||
|
However, the architecture should not prevent future implementation of hardware-assisted or fully hardware-based training.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Inference
|
||||||
|
|
||||||
|
During inference, the host only supplies input data and retrieves the result.
|
||||||
|
|
||||||
|
```text
|
||||||
|
HOST
|
||||||
|
│
|
||||||
|
Input
|
||||||
|
│
|
||||||
|
│ SPI
|
||||||
|
▼
|
||||||
|
┌───────────────┐
|
||||||
|
│ FPGA │
|
||||||
|
│ │
|
||||||
|
│ Neural Network│
|
||||||
|
│ Engine │
|
||||||
|
│ │
|
||||||
|
└───────┬───────┘
|
||||||
|
│
|
||||||
|
Output
|
||||||
|
│
|
||||||
|
│ SPI
|
||||||
|
▼
|
||||||
|
HOST
|
||||||
|
```
|
||||||
|
|
||||||
|
The host is not involved in the individual MAC operations.
|
||||||
|
|
||||||
|
This provides:
|
||||||
|
|
||||||
|
- deterministic computation;
|
||||||
|
- reduced host workload;
|
||||||
|
- hardware parallelism;
|
||||||
|
- predictable latency;
|
||||||
|
- independence from the host CPU architecture.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 12. Multi-Layer Architecture
|
||||||
|
|
||||||
|
The current implementation starts from a single parametrized layer.
|
||||||
|
|
||||||
|
The intended architecture is eventually:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Input
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ Layer 0 │
|
||||||
|
└──────┬───────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ Layer 1 │
|
||||||
|
└──────┬───────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ Layer 2 │
|
||||||
|
└──────┬───────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Output
|
||||||
|
```
|
||||||
|
|
||||||
|
Intermediate data will be stored in FPGA-controlled memory buffers.
|
||||||
|
|
||||||
|
The number and size of layers should ultimately be part of the hardware generation process.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 13. Reusability
|
||||||
|
|
||||||
|
The main purpose of the architecture is reuse.
|
||||||
|
|
||||||
|
A future project should be able to use the same general Neural Network Engine architecture with a different hardware configuration.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Project A
|
||||||
|
N_INPUTS = 32
|
||||||
|
N_NEURONS = 8
|
||||||
|
PARALLEL = 8
|
||||||
|
|
||||||
|
Project B
|
||||||
|
N_INPUTS = 64
|
||||||
|
N_NEURONS = 16
|
||||||
|
PARALLEL = 16
|
||||||
|
|
||||||
|
Project C
|
||||||
|
N_INPUTS = 128
|
||||||
|
N_NEURONS = 32
|
||||||
|
PARALLEL = 32
|
||||||
|
```
|
||||||
|
|
||||||
|
The HDL architecture remains conceptually the same while synthesis parameters generate an implementation appropriate for the target application.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 14. Design Philosophy
|
||||||
|
|
||||||
|
The project should be considered a:
|
||||||
|
|
||||||
|
> **Reusable FPGA Neural Network Accelerator Platform**
|
||||||
|
|
||||||
|
rather than a single neural-network implementation.
|
||||||
|
|
||||||
|
The application determines:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Input size
|
||||||
|
Network topology
|
||||||
|
Number of layers
|
||||||
|
Number of neurons
|
||||||
|
Parallelism
|
||||||
|
Numerical precision
|
||||||
|
Activation functions
|
||||||
|
Memory requirements
|
||||||
|
Performance requirements
|
||||||
|
```
|
||||||
|
|
||||||
|
The hardware generator then produces the corresponding FPGA implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 15. Development Roadmap
|
||||||
|
|
||||||
|
## Phase 1 — Parametric Layer
|
||||||
|
|
||||||
|
- [x] Parametric inputs
|
||||||
|
- [x] Parametric neurons
|
||||||
|
- [x] Parametric parallelism
|
||||||
|
- [x] Accumulation
|
||||||
|
- [x] Bias
|
||||||
|
- [x] ReLU
|
||||||
|
- [x] 32×4 / P=8 functional test
|
||||||
|
|
||||||
|
## Phase 2 — Parameter Sweep
|
||||||
|
|
||||||
|
Validate multiple combinations of:
|
||||||
|
|
||||||
|
```text
|
||||||
|
N_INPUTS
|
||||||
|
N_NEURONS
|
||||||
|
PARALLEL
|
||||||
|
```
|
||||||
|
|
||||||
|
including configurations where the number of inputs is not an exact multiple of the parallelism.
|
||||||
|
|
||||||
|
## Phase 3 — Memory Architecture
|
||||||
|
|
||||||
|
Define:
|
||||||
|
|
||||||
|
- weight memory;
|
||||||
|
- bias memory;
|
||||||
|
- input buffers;
|
||||||
|
- output buffers;
|
||||||
|
- intermediate buffers;
|
||||||
|
- memory addressing;
|
||||||
|
- bandwidth requirements.
|
||||||
|
|
||||||
|
## Phase 4 — SPI Interface
|
||||||
|
|
||||||
|
Implement:
|
||||||
|
|
||||||
|
- SPI controller;
|
||||||
|
- register map;
|
||||||
|
- RAM access;
|
||||||
|
- configuration protocol;
|
||||||
|
- input/output protocol;
|
||||||
|
- status and control.
|
||||||
|
|
||||||
|
## Phase 5 — Multi-Layer Network
|
||||||
|
|
||||||
|
Implement:
|
||||||
|
|
||||||
|
- multiple layers;
|
||||||
|
- intermediate buffers;
|
||||||
|
- layer sequencing;
|
||||||
|
- configurable activation functions.
|
||||||
|
|
||||||
|
## Phase 6 — Host Software
|
||||||
|
|
||||||
|
Develop host-side drivers for:
|
||||||
|
|
||||||
|
- Linux;
|
||||||
|
- ESP32.
|
||||||
|
|
||||||
|
The same FPGA protocol should be usable by both.
|
||||||
|
|
||||||
|
## Phase 7 — Optimization
|
||||||
|
|
||||||
|
Evaluate:
|
||||||
|
|
||||||
|
- pipeline depth;
|
||||||
|
- MAC parallelism;
|
||||||
|
- memory bandwidth;
|
||||||
|
- numerical precision;
|
||||||
|
- FPGA resource utilization;
|
||||||
|
- latency;
|
||||||
|
- throughput.
|
||||||
|
|
||||||
|
## Phase 8 — Optional Hardware Training
|
||||||
|
|
||||||
|
Investigate:
|
||||||
|
|
||||||
|
- backpropagation;
|
||||||
|
- gradient calculation;
|
||||||
|
- weight updates;
|
||||||
|
- hardware-assisted training.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 16. Long-Term Vision
|
||||||
|
|
||||||
|
The final objective is to create a reusable hardware block that can be integrated into different future MIKILAB projects.
|
||||||
|
|
||||||
|
```text
|
||||||
|
APPLICATION
|
||||||
|
│
|
||||||
|
┌─────────────┴─────────────┐
|
||||||
|
│ │
|
||||||
|
Linux ESP32
|
||||||
|
│ │
|
||||||
|
└─────────────┬─────────────┘
|
||||||
|
│
|
||||||
|
SPI / Dual SPI
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────┐
|
||||||
|
│ FPGA │
|
||||||
|
│ │
|
||||||
|
│ Neural Network Engine │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────┐ │
|
||||||
|
│ │ Control │ │
|
||||||
|
│ ├────────────────────┤ │
|
||||||
|
│ │ Input Interface │ │
|
||||||
|
│ ├────────────────────┤ │
|
||||||
|
│ │ NN Compute Core │ │
|
||||||
|
│ ├────────────────────┤ │
|
||||||
|
│ │ Activation │ │
|
||||||
|
│ ├────────────────────┤ │
|
||||||
|
│ │ Output Interface │ │
|
||||||
|
│ └────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Dedicated RAM │
|
||||||
|
│ │
|
||||||
|
└────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
The host platform can change without changing the fundamental Neural Network Engine architecture.
|
||||||
|
|
||||||
|
The FPGA becomes a dedicated neural-computation peripheral, analogous to other hardware accelerators, but optimized specifically for the neural-network topology required by each application.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 17. Current Status
|
||||||
|
|
||||||
|
| Component | Status |
|
||||||
|
|---|---|
|
||||||
|
| Parametric neuron layer | OK Working |
|
||||||
|
| Parametric input count | OK |
|
||||||
|
| Parametric neuron count | OK |
|
||||||
|
| Parametric parallelism | OK |
|
||||||
|
| Accumulation | OK |
|
||||||
|
| Bias | OK |
|
||||||
|
| ReLU | OK |
|
||||||
|
| 32×4 / P=8 validation | OK |
|
||||||
|
| Dedicated RAM architecture | - Design |
|
||||||
|
| SPI interface | Planned |
|
||||||
|
| Dual SPI | Future |
|
||||||
|
| Multi-layer engine | Planned |
|
||||||
|
| Linux host driver | Planned |
|
||||||
|
| ESP32 host driver | Planned |
|
||||||
|
| Hardware training | Future |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core architectural principle
|
||||||
|
|
||||||
|
**The FPGA implements the neural-network machine.
|
||||||
|
The FPGA owns its RAM.
|
||||||
|
The host configures and uses the machine.
|
||||||
|
The network topology is specialized at FPGA build time, while its trained parameters are loaded into FPGA-local memory at initialization.**
|
||||||
|
|
||||||
|
This separation is the foundation of the project.
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
module int8_memory_access #(
|
||||||
|
parameter ADDR_WIDTH = 22
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst,
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// INT8 interface
|
||||||
|
//
|
||||||
|
// addr is BYTE address
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
input wire req,
|
||||||
|
input wire wr,
|
||||||
|
input wire [ADDR_WIDTH-1:0] addr,
|
||||||
|
input wire signed [7:0] wdata,
|
||||||
|
|
||||||
|
output reg signed [7:0] rdata,
|
||||||
|
output reg ready,
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 16-bit memory interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
output reg mem_req,
|
||||||
|
output reg mem_wr,
|
||||||
|
output reg [ADDR_WIDTH-1:0] mem_addr,
|
||||||
|
output reg [15:0] mem_wdata,
|
||||||
|
output reg mem_lb_n,
|
||||||
|
output reg mem_ub_n,
|
||||||
|
|
||||||
|
input wire [15:0] mem_rdata,
|
||||||
|
input wire mem_ready
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// State machine
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
localparam STATE_IDLE = 2'd0;
|
||||||
|
localparam STATE_WAIT = 2'd1;
|
||||||
|
|
||||||
|
reg [1:0] state;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Latched byte address
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg [ADDR_WIDTH-1:0] addr_reg;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Main state machine
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
|
||||||
|
if (rst) begin
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
addr_reg <= {ADDR_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
rdata <= 8'sd0;
|
||||||
|
ready <= 1'b0;
|
||||||
|
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
mem_wr <= 1'b0;
|
||||||
|
mem_addr <= {ADDR_WIDTH{1'b0}};
|
||||||
|
mem_wdata <= 16'h0000;
|
||||||
|
|
||||||
|
// Active-low byte enables:
|
||||||
|
// 1 = disabled
|
||||||
|
mem_lb_n <= 1'b1;
|
||||||
|
mem_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// ready is a one-cycle pulse
|
||||||
|
ready <= 1'b0;
|
||||||
|
|
||||||
|
// mem_req is a one-cycle pulse
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
|
||||||
|
case (state)
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// IDLE
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_IDLE: begin
|
||||||
|
|
||||||
|
if (req) begin
|
||||||
|
|
||||||
|
addr_reg <= addr;
|
||||||
|
|
||||||
|
mem_req <= 1'b1;
|
||||||
|
mem_wr <= wr;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Byte address -> 16-bit word address
|
||||||
|
//
|
||||||
|
// addr[0] = 0 -> low byte
|
||||||
|
// addr[0] = 1 -> high byte
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
mem_addr <= addr >> 1;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Select byte
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
if (addr[0] == 1'b0) begin
|
||||||
|
|
||||||
|
// Low byte
|
||||||
|
mem_lb_n <= 1'b0;
|
||||||
|
mem_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
// Data goes into DQ[7:0]
|
||||||
|
mem_wdata <= {8'h00, wdata};
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// High byte
|
||||||
|
mem_lb_n <= 1'b1;
|
||||||
|
mem_ub_n <= 1'b0;
|
||||||
|
|
||||||
|
// Data goes into DQ[15:8]
|
||||||
|
mem_wdata <= {wdata, 8'h00};
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
state <= STATE_WAIT;
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// WAIT
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_WAIT: begin
|
||||||
|
|
||||||
|
if (mem_ready) begin
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Extract requested byte
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
if (addr_reg[0] == 1'b0)
|
||||||
|
rdata <= mem_rdata[7:0];
|
||||||
|
else
|
||||||
|
rdata <= mem_rdata[15:8];
|
||||||
|
|
||||||
|
ready <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// Default
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
mem_lb_n <= 1'b1;
|
||||||
|
mem_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
module layer #(
|
module layer #(
|
||||||
parameter DATA_WIDTH = 16,
|
parameter DATA_WIDTH = 16,
|
||||||
parameter FRAC_BITS = 8,
|
|
||||||
parameter N_INPUTS = 64,
|
parameter N_INPUTS = 64,
|
||||||
parameter N_NEURONS = 8,
|
parameter N_NEURONS = 8,
|
||||||
parameter PARALLEL = 8,
|
parameter PARALLEL = 8,
|
||||||
@@ -36,7 +35,6 @@ module layer #(
|
|||||||
|
|
||||||
neuron_parallel #(
|
neuron_parallel #(
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
.FRAC_BITS(FRAC_BITS),
|
|
||||||
.N_INPUTS(N_INPUTS),
|
.N_INPUTS(N_INPUTS),
|
||||||
.PARALLEL(PARALLEL),
|
.PARALLEL(PARALLEL),
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
.ACC_WIDTH(ACC_WIDTH)
|
||||||
|
|||||||
+93
-98
@@ -1,122 +1,117 @@
|
|||||||
module mac8 #(
|
module mac8 #(
|
||||||
parameter DATA_WIDTH = 16,
|
parameter DATA_WIDTH = 16,
|
||||||
parameter ACC_WIDTH = 40
|
parameter ACC_WIDTH = 40,
|
||||||
|
parameter PARALLEL = 8
|
||||||
)(
|
)(
|
||||||
input signed [DATA_WIDTH*8-1:0] x_bus,
|
input signed [DATA_WIDTH*PARALLEL-1:0] x_bus,
|
||||||
input signed [DATA_WIDTH*8-1:0] w_bus,
|
input signed [DATA_WIDTH*PARALLEL-1:0] w_bus,
|
||||||
input signed [ACC_WIDTH-1:0] acc_in,
|
input signed [ACC_WIDTH-1:0] acc_in,
|
||||||
output signed [ACC_WIDTH-1:0] acc_out
|
output signed [ACC_WIDTH-1:0] acc_out
|
||||||
);
|
);
|
||||||
|
|
||||||
wire signed [ACC_WIDTH-1:0] m0;
|
/*
|
||||||
wire signed [ACC_WIDTH-1:0] m1;
|
* Each MAC produces one sign-extended product.
|
||||||
wire signed [ACC_WIDTH-1:0] m2;
|
*/
|
||||||
wire signed [ACC_WIDTH-1:0] m3;
|
wire signed [ACC_WIDTH-1:0] products [0:PARALLEL-1];
|
||||||
wire signed [ACC_WIDTH-1:0] m4;
|
|
||||||
wire signed [ACC_WIDTH-1:0] m5;
|
|
||||||
wire signed [ACC_WIDTH-1:0] m6;
|
|
||||||
wire signed [ACC_WIDTH-1:0] m7;
|
|
||||||
|
|
||||||
wire signed [ACC_WIDTH-1:0] s0;
|
genvar i;
|
||||||
wire signed [ACC_WIDTH-1:0] s1;
|
|
||||||
wire signed [ACC_WIDTH-1:0] s2;
|
|
||||||
wire signed [ACC_WIDTH-1:0] s3;
|
|
||||||
|
|
||||||
wire signed [ACC_WIDTH-1:0] s4;
|
generate
|
||||||
wire signed [ACC_WIDTH-1:0] s5;
|
for (i = 0; i < PARALLEL; i = i + 1) begin : GEN_MAC
|
||||||
|
|
||||||
wire signed [ACC_WIDTH-1:0] sum;
|
mac_unit #(
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.ACC_WIDTH(ACC_WIDTH)
|
||||||
|
) u_mac (
|
||||||
|
.x(
|
||||||
|
x_bus[
|
||||||
|
i*DATA_WIDTH
|
||||||
|
+:
|
||||||
|
DATA_WIDTH
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.w(
|
||||||
|
w_bus[
|
||||||
|
i*DATA_WIDTH
|
||||||
|
+:
|
||||||
|
DATA_WIDTH
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.acc_in({ACC_WIDTH{1'b0}}),
|
||||||
|
.acc_out(products[i])
|
||||||
|
);
|
||||||
|
|
||||||
mac_unit #(
|
end
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
endgenerate
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
|
||||||
) mac0 (
|
|
||||||
.x(x_bus[0*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.w(w_bus[0*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m0)
|
|
||||||
);
|
|
||||||
|
|
||||||
mac_unit #(
|
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
|
||||||
) mac1 (
|
|
||||||
.x(x_bus[1*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.w(w_bus[1*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m1)
|
|
||||||
);
|
|
||||||
|
|
||||||
mac_unit #(
|
/*
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
* Balanced binary adder tree.
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
*
|
||||||
) mac2 (
|
* PARALLEL is intended to be a power of two:
|
||||||
.x(x_bus[2*DATA_WIDTH +: DATA_WIDTH]),
|
* 8 -> 3 levels
|
||||||
.w(w_bus[2*DATA_WIDTH +: DATA_WIDTH]),
|
* 16 -> 4 levels
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
* 32 -> 5 levels
|
||||||
.acc_out(m2)
|
*
|
||||||
);
|
* This replaces the previous linear accumulator:
|
||||||
|
*
|
||||||
|
* (((p0+p1)+p2)+p3)+...
|
||||||
|
*
|
||||||
|
* with:
|
||||||
|
*
|
||||||
|
* sum
|
||||||
|
* / \
|
||||||
|
* ... ...
|
||||||
|
*
|
||||||
|
* reducing the combinational depth from O(PARALLEL)
|
||||||
|
* to O(log2(PARALLEL)).
|
||||||
|
*/
|
||||||
|
|
||||||
mac_unit #(
|
localparam TREE_LEVELS = $clog2(PARALLEL);
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
|
||||||
) mac3 (
|
|
||||||
.x(x_bus[3*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.w(w_bus[3*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m3)
|
|
||||||
);
|
|
||||||
|
|
||||||
mac_unit #(
|
wire signed [ACC_WIDTH-1:0]
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
tree [0:TREE_LEVELS][0:PARALLEL-1];
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
|
||||||
) mac4 (
|
|
||||||
.x(x_bus[4*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.w(w_bus[4*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m4)
|
|
||||||
);
|
|
||||||
|
|
||||||
mac_unit #(
|
generate
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
|
||||||
) mac5 (
|
|
||||||
.x(x_bus[5*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.w(w_bus[5*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m5)
|
|
||||||
);
|
|
||||||
|
|
||||||
mac_unit #(
|
/*
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
* Level 0 = individual products
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
*/
|
||||||
) mac6 (
|
for (i = 0; i < PARALLEL; i = i + 1) begin : GEN_TREE_INPUT
|
||||||
.x(x_bus[6*DATA_WIDTH +: DATA_WIDTH]),
|
assign tree[0][i] = products[i];
|
||||||
.w(w_bus[6*DATA_WIDTH +: DATA_WIDTH]),
|
end
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m6)
|
|
||||||
);
|
|
||||||
|
|
||||||
mac_unit #(
|
endgenerate
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
|
||||||
) mac7 (
|
|
||||||
.x(x_bus[7*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.w(w_bus[7*DATA_WIDTH +: DATA_WIDTH]),
|
|
||||||
.acc_in({ACC_WIDTH{1'b0}}),
|
|
||||||
.acc_out(m7)
|
|
||||||
);
|
|
||||||
|
|
||||||
assign s0 = m0 + m1;
|
|
||||||
assign s1 = m2 + m3;
|
|
||||||
assign s2 = m4 + m5;
|
|
||||||
assign s3 = m6 + m7;
|
|
||||||
|
|
||||||
assign s4 = s0 + s1;
|
genvar level;
|
||||||
assign s5 = s2 + s3;
|
genvar node;
|
||||||
|
|
||||||
assign sum = s4 + s5;
|
generate
|
||||||
|
|
||||||
assign acc_out = acc_in + sum;
|
for (level = 0; level < TREE_LEVELS; level = level + 1) begin : GEN_TREE_LEVEL
|
||||||
|
|
||||||
|
for (
|
||||||
|
node = 0;
|
||||||
|
node < (PARALLEL >> (level + 1));
|
||||||
|
node = node + 1
|
||||||
|
) begin : GEN_TREE_NODE
|
||||||
|
|
||||||
|
assign tree[level + 1][node] =
|
||||||
|
tree[level][2*node] +
|
||||||
|
tree[level][2*node + 1];
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the partial sum to the accumulator.
|
||||||
|
*/
|
||||||
|
assign acc_out =
|
||||||
|
acc_in + tree[TREE_LEVELS][0];
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
module memory_interface #(
|
||||||
|
parameter ADDR_WIDTH = 22,
|
||||||
|
parameter DATA_WIDTH = 16
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst,
|
||||||
|
|
||||||
|
input wire req,
|
||||||
|
input wire wr,
|
||||||
|
input wire [ADDR_WIDTH-1:0] addr,
|
||||||
|
input wire [DATA_WIDTH-1:0] wdata,
|
||||||
|
input wire lb_n,
|
||||||
|
input wire ub_n,
|
||||||
|
|
||||||
|
output reg [DATA_WIDTH-1:0] rdata,
|
||||||
|
output reg ready,
|
||||||
|
|
||||||
|
output reg mem_req,
|
||||||
|
output reg mem_wr,
|
||||||
|
output reg [ADDR_WIDTH-1:0] mem_addr,
|
||||||
|
output reg [DATA_WIDTH-1:0] mem_wdata,
|
||||||
|
output reg mem_lb_n,
|
||||||
|
output reg mem_ub_n,
|
||||||
|
|
||||||
|
input wire [DATA_WIDTH-1:0] mem_rdata,
|
||||||
|
input wire mem_ready
|
||||||
|
);
|
||||||
|
localparam STATE_IDLE = 2'd0;
|
||||||
|
localparam STATE_WAIT = 2'd1;
|
||||||
|
|
||||||
|
reg [1:0] state;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (rst) begin
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
rdata <= {DATA_WIDTH{1'b0}};
|
||||||
|
ready <= 1'b0;
|
||||||
|
|
||||||
|
mem_lb_n <= 1'b1;
|
||||||
|
mem_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
mem_wr <= 1'b0;
|
||||||
|
mem_addr <= {ADDR_WIDTH{1'b0}};
|
||||||
|
mem_wdata <= {DATA_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// Default: pulses
|
||||||
|
ready <= 1'b0;
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
|
||||||
|
case (state)
|
||||||
|
|
||||||
|
STATE_IDLE: begin
|
||||||
|
if (req) begin
|
||||||
|
|
||||||
|
// Latch transaction
|
||||||
|
mem_wr <= wr;
|
||||||
|
mem_addr <= addr;
|
||||||
|
mem_wdata <= wdata;
|
||||||
|
mem_lb_n <= lb_n;
|
||||||
|
mem_ub_n <= ub_n;
|
||||||
|
// Issue exactly one-cycle request
|
||||||
|
mem_req <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_WAIT;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
STATE_WAIT: begin
|
||||||
|
|
||||||
|
// Wait for memory completion
|
||||||
|
if (mem_ready) begin
|
||||||
|
|
||||||
|
if (!mem_wr)
|
||||||
|
rdata <= mem_rdata;
|
||||||
|
|
||||||
|
ready <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
module memory_model #(
|
||||||
|
parameter ADDR_WIDTH = 22,
|
||||||
|
parameter DATA_WIDTH = 16,
|
||||||
|
parameter DEPTH = 4096,
|
||||||
|
parameter READ_LATENCY = 2
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst,
|
||||||
|
|
||||||
|
input wire req,
|
||||||
|
input wire wr,
|
||||||
|
input wire [ADDR_WIDTH-1:0] addr,
|
||||||
|
input wire [DATA_WIDTH-1:0] wdata,
|
||||||
|
|
||||||
|
output reg [DATA_WIDTH-1:0] rdata,
|
||||||
|
output reg ready
|
||||||
|
);
|
||||||
|
|
||||||
|
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
|
||||||
|
|
||||||
|
reg busy;
|
||||||
|
reg pending_wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] pending_addr;
|
||||||
|
reg [DATA_WIDTH-1:0] pending_wdata;
|
||||||
|
|
||||||
|
integer delay_count;
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (rst) begin
|
||||||
|
|
||||||
|
rdata <= {DATA_WIDTH{1'b0}};
|
||||||
|
ready <= 1'b0;
|
||||||
|
|
||||||
|
busy <= 1'b0;
|
||||||
|
pending_wr <= 1'b0;
|
||||||
|
pending_addr <= {ADDR_WIDTH{1'b0}};
|
||||||
|
pending_wdata <= {DATA_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
delay_count <= 0;
|
||||||
|
|
||||||
|
for (i = 0; i < DEPTH; i = i + 1)
|
||||||
|
mem[i] <= {DATA_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// ready is a one-cycle pulse
|
||||||
|
ready <= 1'b0;
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Accept request
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
if (!busy) begin
|
||||||
|
|
||||||
|
if (req) begin
|
||||||
|
|
||||||
|
busy <= 1'b1;
|
||||||
|
pending_wr <= wr;
|
||||||
|
pending_addr <= addr;
|
||||||
|
pending_wdata <= wdata;
|
||||||
|
|
||||||
|
delay_count <= READ_LATENCY;
|
||||||
|
end
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Wait
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
if (delay_count > 0) begin
|
||||||
|
|
||||||
|
delay_count <= delay_count - 1;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// --------------------------------------------
|
||||||
|
// Complete transaction
|
||||||
|
// --------------------------------------------
|
||||||
|
|
||||||
|
if (pending_wr) begin
|
||||||
|
|
||||||
|
// WRITE
|
||||||
|
if (pending_addr < DEPTH)
|
||||||
|
mem[pending_addr] <= pending_wdata;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// READ
|
||||||
|
if (pending_addr < DEPTH)
|
||||||
|
rdata <= mem[pending_addr];
|
||||||
|
else
|
||||||
|
rdata <= {DATA_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
ready <= 1'b1;
|
||||||
|
busy <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,404 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module neuron_memory #(
|
||||||
|
parameter ADDR_WIDTH = 22,
|
||||||
|
parameter DATA_WIDTH = 8,
|
||||||
|
parameter N_INPUTS = 32,
|
||||||
|
parameter PARALLEL = 8,
|
||||||
|
parameter ACC_WIDTH = 32
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst,
|
||||||
|
input wire start,
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Memory interface
|
||||||
|
//
|
||||||
|
// BYTE-ADDRESS / INT8 interface
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
output wire mem_req,
|
||||||
|
output wire mem_wr,
|
||||||
|
output wire [ADDR_WIDTH-1:0] mem_addr,
|
||||||
|
output wire signed [7:0] mem_wdata,
|
||||||
|
|
||||||
|
input wire signed [7:0] mem_rdata,
|
||||||
|
input wire mem_ready,
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Network memory layout
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
input wire [ADDR_WIDTH-1:0] x_base,
|
||||||
|
input wire [ADDR_WIDTH-1:0] w_base,
|
||||||
|
input wire [ADDR_WIDTH-1:0] bias_addr,
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Result
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
output reg signed [7:0] y,
|
||||||
|
output reg busy,
|
||||||
|
output reg done
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// STATES
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
localparam STATE_IDLE = 4'd0;
|
||||||
|
localparam STATE_READ_X = 4'd1;
|
||||||
|
localparam STATE_READ_W = 4'd2;
|
||||||
|
localparam STATE_READ_BIAS = 4'd3;
|
||||||
|
localparam STATE_START_N = 4'd4;
|
||||||
|
localparam STATE_WAIT_N = 4'd5;
|
||||||
|
|
||||||
|
reg [3:0] state;
|
||||||
|
|
||||||
|
reg [$clog2(N_INPUTS+1)-1:0] index;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// LOCAL MEMORY ARRAYS
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg signed [7:0] x_mem [0:N_INPUTS-1];
|
||||||
|
reg signed [7:0] w_mem [0:N_INPUTS-1];
|
||||||
|
|
||||||
|
reg signed [7:0] bias_reg;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// NEURON BUS
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire signed [DATA_WIDTH*N_INPUTS-1:0] x_bus;
|
||||||
|
wire signed [DATA_WIDTH*N_INPUTS-1:0] w_bus;
|
||||||
|
|
||||||
|
genvar i;
|
||||||
|
|
||||||
|
generate
|
||||||
|
for (i = 0; i < N_INPUTS; i = i + 1) begin : GEN_BUS
|
||||||
|
|
||||||
|
assign x_bus[i*DATA_WIDTH +: DATA_WIDTH] = x_mem[i];
|
||||||
|
assign w_bus[i*DATA_WIDTH +: DATA_WIDTH] = w_mem[i];
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// INT8 MEMORY ACCESS
|
||||||
|
//
|
||||||
|
// This converts BYTE addresses into 16-bit word accesses.
|
||||||
|
//
|
||||||
|
// IMPORTANT:
|
||||||
|
// The memory side of this block is connected to the EXTERNAL
|
||||||
|
// memory_interface through the neuron_memory ports.
|
||||||
|
//
|
||||||
|
// It must NOT be connected directly to the PSRAM controller.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg access_req;
|
||||||
|
reg access_wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] access_addr;
|
||||||
|
reg signed [7:0] access_wdata;
|
||||||
|
|
||||||
|
wire signed [7:0] access_rdata;
|
||||||
|
wire access_ready;
|
||||||
|
|
||||||
|
wire access_mem_req;
|
||||||
|
wire access_mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] access_mem_addr;
|
||||||
|
wire [15:0] access_mem_wdata;
|
||||||
|
wire access_mem_lb_n;
|
||||||
|
wire access_mem_ub_n;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Return data from the external memory interface.
|
||||||
|
//
|
||||||
|
// memory_interface returns a 16-bit word, while neuron_memory
|
||||||
|
// exposes only the requested INT8 byte.
|
||||||
|
//
|
||||||
|
// int8_memory_access expects the complete 16-bit word.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
wire [15:0] access_mem_rdata;
|
||||||
|
|
||||||
|
assign access_mem_rdata =
|
||||||
|
access_addr[0]
|
||||||
|
? {mem_rdata, 8'h00}
|
||||||
|
: {8'h00, mem_rdata};
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// IMPORTANT:
|
||||||
|
//
|
||||||
|
// mem_ready comes from the EXTERNAL memory_interface.
|
||||||
|
// mem_rdata comes from the EXTERNAL memory_interface.
|
||||||
|
//
|
||||||
|
// This fixes the previous deadlock where int8_memory_access
|
||||||
|
// was waiting for the PSRAM controller's mem_ready directly.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
int8_memory_access #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH)
|
||||||
|
) u_mem (
|
||||||
|
.clk (clk),
|
||||||
|
.rst (rst),
|
||||||
|
|
||||||
|
.req (access_req),
|
||||||
|
.wr (access_wr),
|
||||||
|
.addr (access_addr),
|
||||||
|
.wdata (access_wdata),
|
||||||
|
|
||||||
|
.rdata (access_rdata),
|
||||||
|
.ready (access_ready),
|
||||||
|
|
||||||
|
.mem_req (access_mem_req),
|
||||||
|
.mem_wr (access_mem_wr),
|
||||||
|
.mem_addr (access_mem_addr),
|
||||||
|
.mem_wdata (access_mem_wdata),
|
||||||
|
.mem_lb_n (access_mem_lb_n),
|
||||||
|
.mem_ub_n (access_mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata (access_mem_rdata),
|
||||||
|
.mem_ready (mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// EXTERNAL MEMORY INTERFACE
|
||||||
|
//
|
||||||
|
// The external interface expects the INT8-level signals.
|
||||||
|
// The testbench converts these into its 16-bit bus.
|
||||||
|
//
|
||||||
|
// IMPORTANT:
|
||||||
|
// access_mem_addr is already a WORD address.
|
||||||
|
// However, the external neuron_memory interface is defined
|
||||||
|
// as a BYTE address.
|
||||||
|
//
|
||||||
|
// Therefore expose the original byte address here.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
assign mem_req = access_mem_req;
|
||||||
|
assign mem_wr = access_mem_wr;
|
||||||
|
|
||||||
|
assign mem_addr =
|
||||||
|
access_addr;
|
||||||
|
|
||||||
|
assign mem_wdata =
|
||||||
|
access_wdata;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// NEURON
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg neuron_start;
|
||||||
|
|
||||||
|
wire signed [7:0] neuron_y;
|
||||||
|
wire neuron_busy;
|
||||||
|
wire neuron_done;
|
||||||
|
|
||||||
|
neuron_parallel #(
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.N_INPUTS(N_INPUTS),
|
||||||
|
.PARALLEL(PARALLEL),
|
||||||
|
.ACC_WIDTH(ACC_WIDTH)
|
||||||
|
) u_neuron (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
.start(neuron_start),
|
||||||
|
|
||||||
|
.x_bus(x_bus),
|
||||||
|
.w_bus(w_bus),
|
||||||
|
.bias(bias_reg),
|
||||||
|
|
||||||
|
.y(neuron_y),
|
||||||
|
.busy(neuron_busy),
|
||||||
|
.done(neuron_done)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// CONTROLLER
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
|
||||||
|
if (rst) begin
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
index <= 0;
|
||||||
|
|
||||||
|
bias_reg <= 0;
|
||||||
|
|
||||||
|
access_req <= 1'b0;
|
||||||
|
access_wr <= 1'b0;
|
||||||
|
access_addr <= 0;
|
||||||
|
access_wdata <= 0;
|
||||||
|
|
||||||
|
neuron_start <= 1'b0;
|
||||||
|
|
||||||
|
y <= 0;
|
||||||
|
busy <= 1'b0;
|
||||||
|
done <= 1'b0;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Default pulse signals
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
access_req <= 1'b0;
|
||||||
|
neuron_start <= 1'b0;
|
||||||
|
done <= 1'b0;
|
||||||
|
|
||||||
|
case (state)
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// IDLE
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_IDLE: begin
|
||||||
|
|
||||||
|
busy <= 1'b0;
|
||||||
|
|
||||||
|
if (start) begin
|
||||||
|
|
||||||
|
busy <= 1'b1;
|
||||||
|
index <= 0;
|
||||||
|
|
||||||
|
// First X byte
|
||||||
|
access_addr <= x_base;
|
||||||
|
access_wr <= 1'b0;
|
||||||
|
access_req <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_READ_X;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// READ X
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_READ_X: begin
|
||||||
|
|
||||||
|
if (access_ready) begin
|
||||||
|
|
||||||
|
x_mem[index] <= access_rdata;
|
||||||
|
|
||||||
|
if (index == N_INPUTS-1) begin
|
||||||
|
|
||||||
|
index <= 0;
|
||||||
|
|
||||||
|
access_addr <= w_base;
|
||||||
|
access_wr <= 1'b0;
|
||||||
|
access_req <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_READ_W;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
index <= index + 1'b1;
|
||||||
|
|
||||||
|
access_addr <= x_base + index + 1'b1;
|
||||||
|
access_req <= 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// READ W
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_READ_W: begin
|
||||||
|
|
||||||
|
if (access_ready) begin
|
||||||
|
|
||||||
|
w_mem[index] <= access_rdata;
|
||||||
|
|
||||||
|
if (index == N_INPUTS-1) begin
|
||||||
|
|
||||||
|
access_addr <= bias_addr;
|
||||||
|
access_wr <= 1'b0;
|
||||||
|
access_req <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_READ_BIAS;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
index <= index + 1'b1;
|
||||||
|
|
||||||
|
access_addr <= w_base + index + 1'b1;
|
||||||
|
access_req <= 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// READ BIAS
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_READ_BIAS: begin
|
||||||
|
|
||||||
|
if (access_ready) begin
|
||||||
|
|
||||||
|
bias_reg <= access_rdata;
|
||||||
|
|
||||||
|
state <= STATE_START_N;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// START NEURON
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_START_N: begin
|
||||||
|
|
||||||
|
neuron_start <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_WAIT_N;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// WAIT NEURON
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_WAIT_N: begin
|
||||||
|
|
||||||
|
if (neuron_done) begin
|
||||||
|
|
||||||
|
y <= neuron_y;
|
||||||
|
busy <= 1'b0;
|
||||||
|
done <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// DEFAULT
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
busy <= 1'b0;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
+25
-25
@@ -1,9 +1,8 @@
|
|||||||
module neuron_parallel #(
|
module neuron_parallel #(
|
||||||
parameter DATA_WIDTH = 16,
|
parameter DATA_WIDTH = 8,
|
||||||
parameter FRAC_BITS = 8,
|
parameter N_INPUTS = 32,
|
||||||
parameter N_INPUTS = 64,
|
|
||||||
parameter PARALLEL = 8,
|
parameter PARALLEL = 8,
|
||||||
parameter ACC_WIDTH = 40
|
parameter ACC_WIDTH = 32
|
||||||
)(
|
)(
|
||||||
input clk,
|
input clk,
|
||||||
input rst,
|
input rst,
|
||||||
@@ -31,23 +30,25 @@ module neuron_parallel #(
|
|||||||
|
|
||||||
wire signed [ACC_WIDTH-1:0] acc_next;
|
wire signed [ACC_WIDTH-1:0] acc_next;
|
||||||
|
|
||||||
wire signed [DATA_WIDTH-1:0] bias_ext_small;
|
|
||||||
wire signed [ACC_WIDTH-1:0] bias_ext;
|
wire signed [ACC_WIDTH-1:0] bias_ext;
|
||||||
|
|
||||||
wire signed [ACC_WIDTH-1:0] final_acc;
|
wire signed [ACC_WIDTH-1:0] final_acc;
|
||||||
wire signed [ACC_WIDTH-1:0] final_value;
|
|
||||||
|
|
||||||
assign x_group =
|
assign x_group =
|
||||||
x_bus[group_index*PARALLEL*DATA_WIDTH
|
x_bus[
|
||||||
+: PARALLEL*DATA_WIDTH];
|
group_index*PARALLEL*DATA_WIDTH
|
||||||
|
+: PARALLEL*DATA_WIDTH
|
||||||
|
];
|
||||||
|
|
||||||
assign w_group =
|
assign w_group =
|
||||||
w_bus[group_index*PARALLEL*DATA_WIDTH
|
w_bus[
|
||||||
+: PARALLEL*DATA_WIDTH];
|
group_index*PARALLEL*DATA_WIDTH
|
||||||
|
+: PARALLEL*DATA_WIDTH
|
||||||
|
];
|
||||||
|
|
||||||
mac8 #(
|
mac8 #(
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
.ACC_WIDTH(ACC_WIDTH)
|
.ACC_WIDTH(ACC_WIDTH),
|
||||||
|
.PARALLEL(PARALLEL)
|
||||||
) u_mac8 (
|
) u_mac8 (
|
||||||
.x_bus(x_group),
|
.x_bus(x_group),
|
||||||
.w_bus(w_group),
|
.w_bus(w_group),
|
||||||
@@ -55,17 +56,12 @@ module neuron_parallel #(
|
|||||||
.acc_out(acc_next)
|
.acc_out(acc_next)
|
||||||
);
|
);
|
||||||
|
|
||||||
assign bias_ext_small = bias;
|
// Sign extension INT8 -> INT32
|
||||||
|
|
||||||
assign bias_ext =
|
assign bias_ext =
|
||||||
{{(ACC_WIDTH-DATA_WIDTH){bias_ext_small[DATA_WIDTH-1]}},
|
{{(ACC_WIDTH-DATA_WIDTH){bias[DATA_WIDTH-1]}}, bias};
|
||||||
bias_ext_small};
|
|
||||||
|
|
||||||
assign final_acc =
|
// Accumulazione finale + bias
|
||||||
acc_next + (bias_ext <<< FRAC_BITS);
|
assign final_acc = acc_next + bias_ext;
|
||||||
|
|
||||||
assign final_value =
|
|
||||||
final_acc >>> FRAC_BITS;
|
|
||||||
|
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
|
|
||||||
@@ -93,14 +89,18 @@ module neuron_parallel #(
|
|||||||
|
|
||||||
acc <= final_acc;
|
acc <= final_acc;
|
||||||
|
|
||||||
if (final_value <= 0) begin
|
// ReLU
|
||||||
|
if (final_acc <= 0) begin
|
||||||
y <= 0;
|
y <= 0;
|
||||||
end
|
end
|
||||||
else if (final_value > 32767) begin
|
|
||||||
y <= 16'sh7FFF;
|
// Saturazione INT32 -> INT8
|
||||||
|
else if (final_acc > 127) begin
|
||||||
|
y <= 8'sd127;
|
||||||
end
|
end
|
||||||
|
|
||||||
else begin
|
else begin
|
||||||
y <= final_value[DATA_WIDTH-1:0];
|
y <= final_acc[DATA_WIDTH-1:0];
|
||||||
end
|
end
|
||||||
|
|
||||||
busy <= 0;
|
busy <= 0;
|
||||||
|
|||||||
@@ -0,0 +1,422 @@
|
|||||||
|
module psram_controller #(
|
||||||
|
parameter ADDR_WIDTH = 22,
|
||||||
|
parameter DATA_WIDTH = 16,
|
||||||
|
parameter CLK_FREQ_MHZ = 80
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst,
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Memory Interface side
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
input wire mem_req,
|
||||||
|
input wire mem_wr,
|
||||||
|
input wire [ADDR_WIDTH-1:0] mem_addr,
|
||||||
|
input wire [DATA_WIDTH-1:0] mem_wdata,
|
||||||
|
input wire mem_lb_n,
|
||||||
|
input wire mem_ub_n,
|
||||||
|
|
||||||
|
output reg [DATA_WIDTH-1:0] mem_rdata,
|
||||||
|
output reg mem_ready,
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM physical interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
output reg [ADDR_WIDTH-1:0] psram_a,
|
||||||
|
|
||||||
|
inout wire [DATA_WIDTH-1:0] psram_dq,
|
||||||
|
|
||||||
|
output reg psram_ce_n,
|
||||||
|
output reg psram_oe_n,
|
||||||
|
output reg psram_we_n,
|
||||||
|
output reg psram_lb_n,
|
||||||
|
output reg psram_ub_n,
|
||||||
|
output reg psram_zz_n
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Timing
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
localparam integer ACCESS_CYCLES =
|
||||||
|
((70 * CLK_FREQ_MHZ) + 999) / 1000;
|
||||||
|
|
||||||
|
localparam integer INIT_CYCLES =
|
||||||
|
150 * CLK_FREQ_MHZ;
|
||||||
|
|
||||||
|
localparam integer COUNTER_WIDTH =
|
||||||
|
(INIT_CYCLES <= 1) ? 1 : $clog2(INIT_CYCLES + 1);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// State machine
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
localparam [2:0]
|
||||||
|
STATE_INIT = 3'd0,
|
||||||
|
STATE_IDLE = 3'd1,
|
||||||
|
STATE_READ = 3'd2,
|
||||||
|
STATE_WRITE = 3'd3,
|
||||||
|
STATE_WRITE_WAIT = 3'd4;
|
||||||
|
|
||||||
|
reg [2:0] state;
|
||||||
|
reg [COUNTER_WIDTH-1:0] counter;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Latched transaction
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg [ADDR_WIDTH-1:0] address_reg;
|
||||||
|
reg [DATA_WIDTH-1:0] wdata_reg;
|
||||||
|
reg wr_reg;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Latched byte enables
|
||||||
|
//
|
||||||
|
// Active LOW:
|
||||||
|
// 0 = byte enabled
|
||||||
|
// 1 = byte disabled
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg lb_reg;
|
||||||
|
reg ub_reg;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM data bus control
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg [DATA_WIDTH-1:0] dq_out;
|
||||||
|
reg dq_oe;
|
||||||
|
|
||||||
|
assign psram_dq =
|
||||||
|
dq_oe ? dq_out : {DATA_WIDTH{1'bz}};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Main state machine
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
|
||||||
|
if (rst) begin
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// State
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
state <= STATE_INIT;
|
||||||
|
counter <= 0;
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Transaction registers
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
address_reg <= {ADDR_WIDTH{1'b0}};
|
||||||
|
wdata_reg <= {DATA_WIDTH{1'b0}};
|
||||||
|
wr_reg <= 1'b0;
|
||||||
|
|
||||||
|
// Byte enables disabled during reset
|
||||||
|
lb_reg <= 1'b1;
|
||||||
|
ub_reg <= 1'b1;
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Memory interface
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
mem_rdata <= {DATA_WIDTH{1'b0}};
|
||||||
|
mem_ready <= 1'b0;
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// PSRAM address
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
psram_a <= {ADDR_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// PSRAM control
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b1;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= 1'b1;
|
||||||
|
psram_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Data bus
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
dq_out <= {DATA_WIDTH{1'b0}};
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
// mem_ready is a one-cycle pulse
|
||||||
|
mem_ready <= 1'b0;
|
||||||
|
|
||||||
|
case (state)
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// PSRAM power-up initialization
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_INIT: begin
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b1;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= 1'b1;
|
||||||
|
psram_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
if (counter == INIT_CYCLES - 1) begin
|
||||||
|
|
||||||
|
counter <= 0;
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
counter <= counter + 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// Idle
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_IDLE: begin
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b1;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= 1'b1;
|
||||||
|
psram_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
if (mem_req) begin
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Latch transaction
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
address_reg <= mem_addr;
|
||||||
|
wdata_reg <= mem_wdata;
|
||||||
|
wr_reg <= mem_wr;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Latch byte enables
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
lb_reg <= mem_lb_n;
|
||||||
|
ub_reg <= mem_ub_n;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Address
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
psram_a <= mem_addr;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Apply byte enables immediately
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
psram_lb_n <= mem_lb_n;
|
||||||
|
psram_ub_n <= mem_ub_n;
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b0;
|
||||||
|
|
||||||
|
counter <= 0;
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// WRITE
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
if (mem_wr) begin
|
||||||
|
|
||||||
|
dq_out <= mem_wdata;
|
||||||
|
dq_oe <= 1'b1;
|
||||||
|
|
||||||
|
psram_we_n <= 1'b0;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_WRITE;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// READ
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
else begin
|
||||||
|
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
psram_oe_n <= 1'b0;
|
||||||
|
|
||||||
|
state <= STATE_READ;
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// READ
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_READ: begin
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b0;
|
||||||
|
psram_oe_n <= 1'b0;
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= lb_reg;
|
||||||
|
psram_ub_n <= ub_reg;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
if (counter == ACCESS_CYCLES - 1) begin
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Capture PSRAM data
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
mem_rdata <= psram_dq;
|
||||||
|
mem_ready <= 1'b1;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// End transaction
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b1;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= 1'b1;
|
||||||
|
psram_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
counter <= 0;
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
counter <= counter + 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// WRITE
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_WRITE: begin
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b0;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
psram_we_n <= 1'b0;
|
||||||
|
|
||||||
|
psram_lb_n <= lb_reg;
|
||||||
|
psram_ub_n <= ub_reg;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
dq_oe <= 1'b1;
|
||||||
|
|
||||||
|
if (counter == ACCESS_CYCLES - 1) begin
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// End WE# pulse
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
counter <= 0;
|
||||||
|
state <= STATE_WRITE_WAIT;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
counter <= counter + 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// WRITE WAIT
|
||||||
|
//
|
||||||
|
// Keep CE#/LB#/UB# active for the final write hold
|
||||||
|
// interval before releasing the transaction.
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
STATE_WRITE_WAIT: begin
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b0;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= lb_reg;
|
||||||
|
psram_ub_n <= ub_reg;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Release PSRAM
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b1;
|
||||||
|
psram_lb_n <= 1'b1;
|
||||||
|
psram_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Transaction complete
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
mem_ready <= 1'b1;
|
||||||
|
|
||||||
|
state <= STATE_IDLE;
|
||||||
|
end
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// Default recovery
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
|
||||||
|
state <= STATE_INIT;
|
||||||
|
counter <= 0;
|
||||||
|
|
||||||
|
psram_ce_n <= 1'b1;
|
||||||
|
psram_oe_n <= 1'b1;
|
||||||
|
psram_we_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_lb_n <= 1'b1;
|
||||||
|
psram_ub_n <= 1'b1;
|
||||||
|
|
||||||
|
psram_zz_n <= 1'b1;
|
||||||
|
|
||||||
|
dq_oe <= 1'b0;
|
||||||
|
|
||||||
|
lb_reg <= 1'b1;
|
||||||
|
ub_reg <= 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,954 @@
|
|||||||
|
$date
|
||||||
|
Wed Sep 2 12:39:25 2026
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Icarus Verilog
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1ps
|
||||||
|
$end
|
||||||
|
$scope module tb $end
|
||||||
|
$var wire 16 ! mem_rdata [15:0] $end
|
||||||
|
$var wire 1 " mem_ready $end
|
||||||
|
$var wire 1 # ready $end
|
||||||
|
$var wire 8 $ rdata [7:0] $end
|
||||||
|
$var wire 1 % mem_wr $end
|
||||||
|
$var wire 16 & mem_wdata [15:0] $end
|
||||||
|
$var wire 1 ' mem_ub_n $end
|
||||||
|
$var wire 1 ( mem_req $end
|
||||||
|
$var wire 1 ) mem_lb_n $end
|
||||||
|
$var wire 22 * mem_addr [21:0] $end
|
||||||
|
$var parameter 32 + ADDR_WIDTH $end
|
||||||
|
$var real 1 , CLK_PERIOD $end
|
||||||
|
$var reg 22 - addr [21:0] $end
|
||||||
|
$var reg 1 . clk $end
|
||||||
|
$var reg 16 / model_rdata [15:0] $end
|
||||||
|
$var reg 1 0 model_ready $end
|
||||||
|
$var reg 1 1 req $end
|
||||||
|
$var reg 1 2 rst $end
|
||||||
|
$var reg 8 3 wdata [7:0] $end
|
||||||
|
$var reg 1 4 wr $end
|
||||||
|
$var integer 32 5 i [31:0] $end
|
||||||
|
$scope module dut $end
|
||||||
|
$var wire 22 6 addr [21:0] $end
|
||||||
|
$var wire 1 . clk $end
|
||||||
|
$var wire 16 7 mem_rdata [15:0] $end
|
||||||
|
$var wire 1 " mem_ready $end
|
||||||
|
$var wire 1 1 req $end
|
||||||
|
$var wire 1 2 rst $end
|
||||||
|
$var wire 8 8 wdata [7:0] $end
|
||||||
|
$var wire 1 4 wr $end
|
||||||
|
$var parameter 32 9 ADDR_WIDTH $end
|
||||||
|
$var parameter 2 : STATE_IDLE $end
|
||||||
|
$var parameter 2 ; STATE_WAIT $end
|
||||||
|
$var reg 22 < addr_reg [21:0] $end
|
||||||
|
$var reg 22 = mem_addr [21:0] $end
|
||||||
|
$var reg 1 ) mem_lb_n $end
|
||||||
|
$var reg 1 ( mem_req $end
|
||||||
|
$var reg 1 ' mem_ub_n $end
|
||||||
|
$var reg 16 > mem_wdata [15:0] $end
|
||||||
|
$var reg 1 % mem_wr $end
|
||||||
|
$var reg 8 ? rdata [7:0] $end
|
||||||
|
$var reg 1 # ready $end
|
||||||
|
$var reg 2 @ state [1:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$scope task read_byte $end
|
||||||
|
$var reg 22 A byte_addr [21:0] $end
|
||||||
|
$var reg 8 B expected [7:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$scope task write_byte $end
|
||||||
|
$var reg 22 C byte_addr [21:0] $end
|
||||||
|
$var reg 8 D data [7:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$upscope $end
|
||||||
|
$enddefinitions $end
|
||||||
|
$comment Show the parameter values. $end
|
||||||
|
$dumpall
|
||||||
|
b1 ;
|
||||||
|
b0 :
|
||||||
|
b10110 9
|
||||||
|
r12.5 ,
|
||||||
|
b10110 +
|
||||||
|
$end
|
||||||
|
#0
|
||||||
|
$dumpvars
|
||||||
|
bx D
|
||||||
|
bx C
|
||||||
|
bx B
|
||||||
|
bx A
|
||||||
|
bx @
|
||||||
|
bx ?
|
||||||
|
bx >
|
||||||
|
bx =
|
||||||
|
bx <
|
||||||
|
b0 8
|
||||||
|
b0 7
|
||||||
|
b0 6
|
||||||
|
b10000000000 5
|
||||||
|
04
|
||||||
|
b0 3
|
||||||
|
12
|
||||||
|
01
|
||||||
|
00
|
||||||
|
b0 /
|
||||||
|
0.
|
||||||
|
b0 -
|
||||||
|
bx *
|
||||||
|
x)
|
||||||
|
x(
|
||||||
|
x'
|
||||||
|
bx &
|
||||||
|
x%
|
||||||
|
bx $
|
||||||
|
x#
|
||||||
|
0"
|
||||||
|
b0 !
|
||||||
|
$end
|
||||||
|
#6250
|
||||||
|
1'
|
||||||
|
1)
|
||||||
|
b0 &
|
||||||
|
b0 >
|
||||||
|
b0 *
|
||||||
|
b0 =
|
||||||
|
0%
|
||||||
|
0(
|
||||||
|
0#
|
||||||
|
b0 $
|
||||||
|
b0 ?
|
||||||
|
b0 <
|
||||||
|
b0 @
|
||||||
|
1.
|
||||||
|
#12500
|
||||||
|
0.
|
||||||
|
#18750
|
||||||
|
1.
|
||||||
|
#25000
|
||||||
|
0.
|
||||||
|
#31250
|
||||||
|
1.
|
||||||
|
#37500
|
||||||
|
0.
|
||||||
|
#43750
|
||||||
|
1.
|
||||||
|
#50000
|
||||||
|
0.
|
||||||
|
#56250
|
||||||
|
b10010 D
|
||||||
|
b0 C
|
||||||
|
02
|
||||||
|
1.
|
||||||
|
#62500
|
||||||
|
0.
|
||||||
|
#68750
|
||||||
|
11
|
||||||
|
14
|
||||||
|
b10010 3
|
||||||
|
b10010 8
|
||||||
|
1.
|
||||||
|
#75000
|
||||||
|
0.
|
||||||
|
#81250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b10010 &
|
||||||
|
b10010 >
|
||||||
|
0)
|
||||||
|
1%
|
||||||
|
1(
|
||||||
|
1.
|
||||||
|
#87500
|
||||||
|
0.
|
||||||
|
#93750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#100000
|
||||||
|
0.
|
||||||
|
#106250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#112500
|
||||||
|
0.
|
||||||
|
#118750
|
||||||
|
0#
|
||||||
|
b110100 D
|
||||||
|
b1 C
|
||||||
|
1.
|
||||||
|
#125000
|
||||||
|
0.
|
||||||
|
#131250
|
||||||
|
11
|
||||||
|
b110100 3
|
||||||
|
b110100 8
|
||||||
|
b1 -
|
||||||
|
b1 6
|
||||||
|
1.
|
||||||
|
#137500
|
||||||
|
0.
|
||||||
|
#143750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b11010000000000 &
|
||||||
|
b11010000000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b1 <
|
||||||
|
1.
|
||||||
|
#150000
|
||||||
|
0.
|
||||||
|
#156250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#162500
|
||||||
|
0.
|
||||||
|
#168750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
1.
|
||||||
|
#175000
|
||||||
|
0.
|
||||||
|
#181250
|
||||||
|
0#
|
||||||
|
b1010110 D
|
||||||
|
b10 C
|
||||||
|
1.
|
||||||
|
#187500
|
||||||
|
0.
|
||||||
|
#193750
|
||||||
|
11
|
||||||
|
b1010110 3
|
||||||
|
b1010110 8
|
||||||
|
b10 -
|
||||||
|
b10 6
|
||||||
|
1.
|
||||||
|
#200000
|
||||||
|
0.
|
||||||
|
#206250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1010110 &
|
||||||
|
b1010110 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b1 *
|
||||||
|
b1 =
|
||||||
|
1(
|
||||||
|
b10 <
|
||||||
|
1.
|
||||||
|
#212500
|
||||||
|
0.
|
||||||
|
#218750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#225000
|
||||||
|
0.
|
||||||
|
#231250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#237500
|
||||||
|
0.
|
||||||
|
#243750
|
||||||
|
0#
|
||||||
|
b1111000 D
|
||||||
|
b11 C
|
||||||
|
1.
|
||||||
|
#250000
|
||||||
|
0.
|
||||||
|
#256250
|
||||||
|
11
|
||||||
|
b1111000 3
|
||||||
|
b1111000 8
|
||||||
|
b11 -
|
||||||
|
b11 6
|
||||||
|
1.
|
||||||
|
#262500
|
||||||
|
0.
|
||||||
|
#268750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b111100000000000 &
|
||||||
|
b111100000000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b11 <
|
||||||
|
1.
|
||||||
|
#275000
|
||||||
|
0.
|
||||||
|
#281250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#287500
|
||||||
|
0.
|
||||||
|
#293750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
1.
|
||||||
|
#300000
|
||||||
|
0.
|
||||||
|
#306250
|
||||||
|
0#
|
||||||
|
b10010 B
|
||||||
|
b0 A
|
||||||
|
1.
|
||||||
|
#312500
|
||||||
|
0.
|
||||||
|
#318750
|
||||||
|
11
|
||||||
|
04
|
||||||
|
b0 -
|
||||||
|
b0 6
|
||||||
|
1.
|
||||||
|
#325000
|
||||||
|
0.
|
||||||
|
#331250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1111000 &
|
||||||
|
b1111000 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b0 *
|
||||||
|
b0 =
|
||||||
|
0%
|
||||||
|
1(
|
||||||
|
b0 <
|
||||||
|
1.
|
||||||
|
#337500
|
||||||
|
0.
|
||||||
|
#343750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
b11010000010010 !
|
||||||
|
b11010000010010 7
|
||||||
|
b11010000010010 /
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#350000
|
||||||
|
0.
|
||||||
|
#356250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b10010 $
|
||||||
|
b10010 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#362500
|
||||||
|
0.
|
||||||
|
#368750
|
||||||
|
0#
|
||||||
|
b110100 B
|
||||||
|
b1 A
|
||||||
|
1.
|
||||||
|
#375000
|
||||||
|
0.
|
||||||
|
#381250
|
||||||
|
11
|
||||||
|
b1 -
|
||||||
|
b1 6
|
||||||
|
1.
|
||||||
|
#387500
|
||||||
|
0.
|
||||||
|
#393750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b111100000000000 &
|
||||||
|
b111100000000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b1 <
|
||||||
|
1.
|
||||||
|
#400000
|
||||||
|
0.
|
||||||
|
#406250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#412500
|
||||||
|
0.
|
||||||
|
#418750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b110100 $
|
||||||
|
b110100 ?
|
||||||
|
1.
|
||||||
|
#425000
|
||||||
|
0.
|
||||||
|
#431250
|
||||||
|
0#
|
||||||
|
b1010110 B
|
||||||
|
b10 A
|
||||||
|
1.
|
||||||
|
#437500
|
||||||
|
0.
|
||||||
|
#443750
|
||||||
|
11
|
||||||
|
b10 -
|
||||||
|
b10 6
|
||||||
|
1.
|
||||||
|
#450000
|
||||||
|
0.
|
||||||
|
#456250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1111000 &
|
||||||
|
b1111000 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b1 *
|
||||||
|
b1 =
|
||||||
|
1(
|
||||||
|
b10 <
|
||||||
|
1.
|
||||||
|
#462500
|
||||||
|
0.
|
||||||
|
#468750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
b111100001010110 !
|
||||||
|
b111100001010110 7
|
||||||
|
b111100001010110 /
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#475000
|
||||||
|
0.
|
||||||
|
#481250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1010110 $
|
||||||
|
b1010110 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#487500
|
||||||
|
0.
|
||||||
|
#493750
|
||||||
|
0#
|
||||||
|
b1111000 B
|
||||||
|
b11 A
|
||||||
|
1.
|
||||||
|
#500000
|
||||||
|
0.
|
||||||
|
#506250
|
||||||
|
11
|
||||||
|
b11 -
|
||||||
|
b11 6
|
||||||
|
1.
|
||||||
|
#512500
|
||||||
|
0.
|
||||||
|
#518750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b111100000000000 &
|
||||||
|
b111100000000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b11 <
|
||||||
|
1.
|
||||||
|
#525000
|
||||||
|
0.
|
||||||
|
#531250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#537500
|
||||||
|
0.
|
||||||
|
#543750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1111000 $
|
||||||
|
b1111000 ?
|
||||||
|
1.
|
||||||
|
#550000
|
||||||
|
0.
|
||||||
|
#556250
|
||||||
|
0#
|
||||||
|
b110100 D
|
||||||
|
b10000 C
|
||||||
|
1.
|
||||||
|
#562500
|
||||||
|
0.
|
||||||
|
#568750
|
||||||
|
11
|
||||||
|
14
|
||||||
|
b110100 3
|
||||||
|
b110100 8
|
||||||
|
b10000 -
|
||||||
|
b10000 6
|
||||||
|
1.
|
||||||
|
#575000
|
||||||
|
0.
|
||||||
|
#581250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b110100 &
|
||||||
|
b110100 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b1000 *
|
||||||
|
b1000 =
|
||||||
|
1%
|
||||||
|
1(
|
||||||
|
b10000 <
|
||||||
|
1.
|
||||||
|
#587500
|
||||||
|
0.
|
||||||
|
#593750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#600000
|
||||||
|
0.
|
||||||
|
#606250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1010110 $
|
||||||
|
b1010110 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#612500
|
||||||
|
0.
|
||||||
|
#618750
|
||||||
|
0#
|
||||||
|
b10010 D
|
||||||
|
b10001 C
|
||||||
|
1.
|
||||||
|
#625000
|
||||||
|
0.
|
||||||
|
#631250
|
||||||
|
11
|
||||||
|
b10010 3
|
||||||
|
b10010 8
|
||||||
|
b10001 -
|
||||||
|
b10001 6
|
||||||
|
1.
|
||||||
|
#637500
|
||||||
|
0.
|
||||||
|
#643750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1001000000000 &
|
||||||
|
b1001000000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b10001 <
|
||||||
|
1.
|
||||||
|
#650000
|
||||||
|
0.
|
||||||
|
#656250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#662500
|
||||||
|
0.
|
||||||
|
#668750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1111000 $
|
||||||
|
b1111000 ?
|
||||||
|
1.
|
||||||
|
#675000
|
||||||
|
0.
|
||||||
|
#681250
|
||||||
|
0#
|
||||||
|
b10101010 D
|
||||||
|
b10000 C
|
||||||
|
1.
|
||||||
|
#687500
|
||||||
|
0.
|
||||||
|
#693750
|
||||||
|
11
|
||||||
|
b10101010 3
|
||||||
|
b10101010 8
|
||||||
|
b10000 -
|
||||||
|
b10000 6
|
||||||
|
1.
|
||||||
|
#700000
|
||||||
|
0.
|
||||||
|
#706250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b10101010 &
|
||||||
|
b10101010 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
1(
|
||||||
|
b10000 <
|
||||||
|
1.
|
||||||
|
#712500
|
||||||
|
0.
|
||||||
|
#718750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#725000
|
||||||
|
0.
|
||||||
|
#731250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1010110 $
|
||||||
|
b1010110 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#737500
|
||||||
|
0.
|
||||||
|
#743750
|
||||||
|
0#
|
||||||
|
b10111011 D
|
||||||
|
b10001 C
|
||||||
|
1.
|
||||||
|
#750000
|
||||||
|
0.
|
||||||
|
#756250
|
||||||
|
11
|
||||||
|
b10111011 3
|
||||||
|
b10111011 8
|
||||||
|
b10001 -
|
||||||
|
b10001 6
|
||||||
|
1.
|
||||||
|
#762500
|
||||||
|
0.
|
||||||
|
#768750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1011101100000000 &
|
||||||
|
b1011101100000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b10001 <
|
||||||
|
1.
|
||||||
|
#775000
|
||||||
|
0.
|
||||||
|
#781250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#787500
|
||||||
|
0.
|
||||||
|
#793750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1111000 $
|
||||||
|
b1111000 ?
|
||||||
|
1.
|
||||||
|
#800000
|
||||||
|
0.
|
||||||
|
#806250
|
||||||
|
0#
|
||||||
|
b11111111 D
|
||||||
|
b100000 C
|
||||||
|
1.
|
||||||
|
#812500
|
||||||
|
0.
|
||||||
|
#818750
|
||||||
|
11
|
||||||
|
b11111111 3
|
||||||
|
b11111111 8
|
||||||
|
b100000 -
|
||||||
|
b100000 6
|
||||||
|
1.
|
||||||
|
#825000
|
||||||
|
0.
|
||||||
|
#831250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b11111111 &
|
||||||
|
b11111111 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b10000 *
|
||||||
|
b10000 =
|
||||||
|
1(
|
||||||
|
b100000 <
|
||||||
|
1.
|
||||||
|
#837500
|
||||||
|
0.
|
||||||
|
#843750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#850000
|
||||||
|
0.
|
||||||
|
#856250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1010110 $
|
||||||
|
b1010110 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#862500
|
||||||
|
0.
|
||||||
|
#868750
|
||||||
|
0#
|
||||||
|
b10000000 D
|
||||||
|
b100001 C
|
||||||
|
1.
|
||||||
|
#875000
|
||||||
|
0.
|
||||||
|
#881250
|
||||||
|
11
|
||||||
|
b10000000 3
|
||||||
|
b10000000 8
|
||||||
|
b100001 -
|
||||||
|
b100001 6
|
||||||
|
1.
|
||||||
|
#887500
|
||||||
|
0.
|
||||||
|
#893750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1000000000000000 &
|
||||||
|
b1000000000000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b100001 <
|
||||||
|
1.
|
||||||
|
#900000
|
||||||
|
0.
|
||||||
|
#906250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
1.
|
||||||
|
#912500
|
||||||
|
0.
|
||||||
|
#918750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1111000 $
|
||||||
|
b1111000 ?
|
||||||
|
1.
|
||||||
|
#925000
|
||||||
|
0.
|
||||||
|
#931250
|
||||||
|
0#
|
||||||
|
b1111111 D
|
||||||
|
b100010 C
|
||||||
|
1.
|
||||||
|
#937500
|
||||||
|
0.
|
||||||
|
#943750
|
||||||
|
11
|
||||||
|
b1111111 3
|
||||||
|
b1111111 8
|
||||||
|
b100010 -
|
||||||
|
b100010 6
|
||||||
|
1.
|
||||||
|
#950000
|
||||||
|
0.
|
||||||
|
#956250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1111111 &
|
||||||
|
b1111111 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b10001 *
|
||||||
|
b10001 =
|
||||||
|
1(
|
||||||
|
b100010 <
|
||||||
|
1.
|
||||||
|
#962500
|
||||||
|
0.
|
||||||
|
#968750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#975000
|
||||||
|
0.
|
||||||
|
#981250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1010110 $
|
||||||
|
b1010110 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#987500
|
||||||
|
0.
|
||||||
|
#993750
|
||||||
|
0#
|
||||||
|
b11111111 B
|
||||||
|
b100000 A
|
||||||
|
1.
|
||||||
|
#1000000
|
||||||
|
0.
|
||||||
|
#1006250
|
||||||
|
11
|
||||||
|
04
|
||||||
|
b100000 -
|
||||||
|
b100000 6
|
||||||
|
1.
|
||||||
|
#1012500
|
||||||
|
0.
|
||||||
|
#1018750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b10000 *
|
||||||
|
b10000 =
|
||||||
|
0%
|
||||||
|
1(
|
||||||
|
b100000 <
|
||||||
|
1.
|
||||||
|
#1025000
|
||||||
|
0.
|
||||||
|
#1031250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
b1000000011111111 !
|
||||||
|
b1000000011111111 7
|
||||||
|
b1000000011111111 /
|
||||||
|
1.
|
||||||
|
#1037500
|
||||||
|
0.
|
||||||
|
#1043750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b11111111 $
|
||||||
|
b11111111 ?
|
||||||
|
1.
|
||||||
|
#1050000
|
||||||
|
0.
|
||||||
|
#1056250
|
||||||
|
0#
|
||||||
|
b10000000 B
|
||||||
|
b100001 A
|
||||||
|
1.
|
||||||
|
#1062500
|
||||||
|
0.
|
||||||
|
#1068750
|
||||||
|
11
|
||||||
|
b100001 -
|
||||||
|
b100001 6
|
||||||
|
1.
|
||||||
|
#1075000
|
||||||
|
0.
|
||||||
|
#1081250
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b111111100000000 &
|
||||||
|
b111111100000000 >
|
||||||
|
0'
|
||||||
|
1)
|
||||||
|
1(
|
||||||
|
b100001 <
|
||||||
|
1.
|
||||||
|
#1087500
|
||||||
|
0.
|
||||||
|
#1093750
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
0(
|
||||||
|
1.
|
||||||
|
#1100000
|
||||||
|
0.
|
||||||
|
#1106250
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b10000000 $
|
||||||
|
b10000000 ?
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
1.
|
||||||
|
#1112500
|
||||||
|
0.
|
||||||
|
#1118750
|
||||||
|
0#
|
||||||
|
b1111111 B
|
||||||
|
b100010 A
|
||||||
|
1.
|
||||||
|
#1125000
|
||||||
|
0.
|
||||||
|
#1131250
|
||||||
|
11
|
||||||
|
b100010 -
|
||||||
|
b100010 6
|
||||||
|
1.
|
||||||
|
#1137500
|
||||||
|
0.
|
||||||
|
#1143750
|
||||||
|
01
|
||||||
|
b1 @
|
||||||
|
b1111111 &
|
||||||
|
b1111111 >
|
||||||
|
1'
|
||||||
|
0)
|
||||||
|
b10001 *
|
||||||
|
b10001 =
|
||||||
|
1(
|
||||||
|
b100010 <
|
||||||
|
1.
|
||||||
|
#1150000
|
||||||
|
0.
|
||||||
|
#1156250
|
||||||
|
0(
|
||||||
|
1"
|
||||||
|
10
|
||||||
|
b1111111 !
|
||||||
|
b1111111 7
|
||||||
|
b1111111 /
|
||||||
|
1.
|
||||||
|
#1162500
|
||||||
|
0.
|
||||||
|
#1168750
|
||||||
|
0"
|
||||||
|
00
|
||||||
|
b0 @
|
||||||
|
1#
|
||||||
|
b1111111 $
|
||||||
|
b1111111 ?
|
||||||
|
1.
|
||||||
|
#1175000
|
||||||
|
0.
|
||||||
|
#1181250
|
||||||
|
0#
|
||||||
|
1.
|
||||||
Executable
+550
@@ -0,0 +1,550 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x102cce1c0 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x102cc09e0 .scope module, "tb" "tb" 3 3;
|
||||||
|
.timescale -9 -12;
|
||||||
|
P_0x102ccf000 .param/l "ADDR_WIDTH" 1 3 5, +C4<00000000000000000000000000010110>;
|
||||||
|
P_0x102ccf040 .param/real "CLK_PERIOD" 1 3 6, Cr<m6400000000000000gfc5>; value=12.5000
|
||||||
|
L_0x102ccff60 .functor BUFZ 16, v0x74b08cdc0_0, C4<0000000000000000>, C4<0000000000000000>, C4<0000000000000000>;
|
||||||
|
L_0x102ccf620 .functor BUFZ 1, v0x74b08ce60_0, C4<0>, C4<0>, C4<0>;
|
||||||
|
v0x74b08c640_0 .var "addr", 21 0;
|
||||||
|
v0x74b08c6e0_0 .var "clk", 0 0;
|
||||||
|
v0x74b08c780_0 .var/i "i", 31 0;
|
||||||
|
v0x74b08c820_0 .net "mem_addr", 21 0, v0x102ccd820_0; 1 drivers
|
||||||
|
v0x74b08c8c0_0 .net "mem_lb_n", 0 0, v0x102ccf580_0; 1 drivers
|
||||||
|
v0x74b08c960_0 .net "mem_rdata", 15 0, L_0x102ccff60; 1 drivers
|
||||||
|
v0x74b08ca00_0 .net "mem_ready", 0 0, L_0x102ccf620; 1 drivers
|
||||||
|
v0x74b08caa0_0 .net "mem_req", 0 0, v0x102ccfb40_0; 1 drivers
|
||||||
|
v0x74b08cb40_0 .net "mem_ub_n", 0 0, v0x102ccfbe0_0; 1 drivers
|
||||||
|
v0x74b08cbe0_0 .net "mem_wdata", 15 0, v0x102ccfc80_0; 1 drivers
|
||||||
|
v0x74b08cc80_0 .net "mem_wr", 0 0, v0x102ccfd20_0; 1 drivers
|
||||||
|
v0x74b08cd20 .array "memory", 1023 0, 15 0;
|
||||||
|
v0x74b08cdc0_0 .var "model_rdata", 15 0;
|
||||||
|
v0x74b08ce60_0 .var "model_ready", 0 0;
|
||||||
|
v0x74b08cf00_0 .net/s "rdata", 7 0, v0x102ccfdc0_0; 1 drivers
|
||||||
|
v0x74b08cfa0_0 .net "ready", 0 0, v0x74b08c000_0; 1 drivers
|
||||||
|
v0x74b08d040_0 .var "req", 0 0;
|
||||||
|
v0x74b08d0e0_0 .var "rst", 0 0;
|
||||||
|
v0x74b08d180_0 .var/s "wdata", 7 0;
|
||||||
|
v0x74b08d220_0 .var "wr", 0 0;
|
||||||
|
S_0x102cc0b60 .scope module, "dut" "int8_memory_access" 3 43, 4 1 0, S_0x102cc09e0;
|
||||||
|
.timescale -9 -12;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
.port_info 2 /INPUT 1 "req";
|
||||||
|
.port_info 3 /INPUT 1 "wr";
|
||||||
|
.port_info 4 /INPUT 22 "addr";
|
||||||
|
.port_info 5 /INPUT 8 "wdata";
|
||||||
|
.port_info 6 /OUTPUT 8 "rdata";
|
||||||
|
.port_info 7 /OUTPUT 1 "ready";
|
||||||
|
.port_info 8 /OUTPUT 1 "mem_req";
|
||||||
|
.port_info 9 /OUTPUT 1 "mem_wr";
|
||||||
|
.port_info 10 /OUTPUT 22 "mem_addr";
|
||||||
|
.port_info 11 /OUTPUT 16 "mem_wdata";
|
||||||
|
.port_info 12 /OUTPUT 1 "mem_lb_n";
|
||||||
|
.port_info 13 /OUTPUT 1 "mem_ub_n";
|
||||||
|
.port_info 14 /INPUT 16 "mem_rdata";
|
||||||
|
.port_info 15 /INPUT 1 "mem_ready";
|
||||||
|
P_0x102cce340 .param/l "ADDR_WIDTH" 0 4 2, +C4<00000000000000000000000000010110>;
|
||||||
|
P_0x102cce380 .param/l "STATE_IDLE" 1 4 40, C4<00>;
|
||||||
|
P_0x102cce3c0 .param/l "STATE_WAIT" 1 4 41, C4<01>;
|
||||||
|
v0x102ccb170_0 .net "addr", 21 0, v0x74b08c640_0; 1 drivers
|
||||||
|
v0x102ccd070_0 .var "addr_reg", 21 0;
|
||||||
|
v0x102ccebc0_0 .net "clk", 0 0, v0x74b08c6e0_0; 1 drivers
|
||||||
|
v0x102ccd820_0 .var "mem_addr", 21 0;
|
||||||
|
v0x102ccf580_0 .var "mem_lb_n", 0 0;
|
||||||
|
v0x102ccfa00_0 .net "mem_rdata", 15 0, L_0x102ccff60; alias, 1 drivers
|
||||||
|
v0x102ccfaa0_0 .net "mem_ready", 0 0, L_0x102ccf620; alias, 1 drivers
|
||||||
|
v0x102ccfb40_0 .var "mem_req", 0 0;
|
||||||
|
v0x102ccfbe0_0 .var "mem_ub_n", 0 0;
|
||||||
|
v0x102ccfc80_0 .var "mem_wdata", 15 0;
|
||||||
|
v0x102ccfd20_0 .var "mem_wr", 0 0;
|
||||||
|
v0x102ccfdc0_0 .var/s "rdata", 7 0;
|
||||||
|
v0x74b08c000_0 .var "ready", 0 0;
|
||||||
|
v0x74b08c0a0_0 .net "req", 0 0, v0x74b08d040_0; 1 drivers
|
||||||
|
v0x74b08c140_0 .net "rst", 0 0, v0x74b08d0e0_0; 1 drivers
|
||||||
|
v0x74b08c1e0_0 .var "state", 1 0;
|
||||||
|
v0x74b08c280_0 .net/s "wdata", 7 0, v0x74b08d180_0; 1 drivers
|
||||||
|
v0x74b08c320_0 .net "wr", 0 0, v0x74b08d220_0; 1 drivers
|
||||||
|
E_0x74ac1b040 .event posedge, v0x102ccebc0_0;
|
||||||
|
S_0x102cd1180 .scope task, "read_byte" "read_byte" 3 174, 3 174 0, S_0x102cc09e0;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x74b08c3c0_0 .var "byte_addr", 21 0;
|
||||||
|
v0x74b08c460_0 .var/s "expected", 7 0;
|
||||||
|
E_0x74ac1b080 .event anyedge, v0x74b08c000_0;
|
||||||
|
TD_tb.read_byte ;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%load/vec4 v0x74b08c3c0_0;
|
||||||
|
%assign/vec4 v0x74b08c640_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08d220_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08d040_0, 0;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08d040_0, 0;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x74b08cfa0_0;
|
||||||
|
%cmpi/ne 1, 0, 1;
|
||||||
|
%jmp/0xz T_0.1, 6;
|
||||||
|
%wait E_0x74ac1b080;
|
||||||
|
%jmp T_0.0;
|
||||||
|
T_0.1 ;
|
||||||
|
%load/vec4 v0x74b08cf00_0;
|
||||||
|
%load/vec4 v0x74b08c460_0;
|
||||||
|
%cmp/ne;
|
||||||
|
%jmp/0xz T_0.2, 6;
|
||||||
|
%vpi_call/w 3 195 "$display", "READ BYTE addr=0x%08x FAIL got=0x%02x expected=0x%02x", v0x74b08c3c0_0, v0x74b08cf00_0, v0x74b08c460_0 {0 0 0};
|
||||||
|
%vpi_call/w 3 202 "$fatal" {0 0 0};
|
||||||
|
%jmp T_0.3;
|
||||||
|
T_0.2 ;
|
||||||
|
%vpi_call/w 3 206 "$display", "READ BYTE addr=0x%08x data=0x%02x PASS", v0x74b08c3c0_0, v0x74b08cf00_0 {0 0 0};
|
||||||
|
T_0.3 ;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%end;
|
||||||
|
S_0x102cd1300 .scope task, "write_byte" "write_byte" 3 138, 3 138 0, S_0x102cc09e0;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x74b08c500_0 .var "byte_addr", 21 0;
|
||||||
|
v0x74b08c5a0_0 .var/s "data", 7 0;
|
||||||
|
TD_tb.write_byte ;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%load/vec4 v0x74b08c500_0;
|
||||||
|
%assign/vec4 v0x74b08c640_0, 0;
|
||||||
|
%load/vec4 v0x74b08c5a0_0;
|
||||||
|
%assign/vec4 v0x74b08d180_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08d220_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08d040_0, 0;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08d040_0, 0;
|
||||||
|
T_1.4 ;
|
||||||
|
%load/vec4 v0x74b08cfa0_0;
|
||||||
|
%cmpi/ne 1, 0, 1;
|
||||||
|
%jmp/0xz T_1.5, 6;
|
||||||
|
%wait E_0x74ac1b080;
|
||||||
|
%jmp T_1.4;
|
||||||
|
T_1.5 ;
|
||||||
|
%vpi_call/w 3 158 "$display", "WRITE BYTE addr=0x%08x data=0x%02x PASS", v0x74b08c500_0, v0x74b08c5a0_0 {0 0 0};
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%end;
|
||||||
|
.scope S_0x102cc0b60;
|
||||||
|
T_2 ;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%load/vec4 v0x74b08c140_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%assign/vec4 v0x74b08c1e0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%assign/vec4 v0x102ccd070_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 8;
|
||||||
|
%assign/vec4 v0x102ccfdc0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08c000_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfb40_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfd20_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%assign/vec4 v0x102ccd820_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%assign/vec4 v0x102ccfc80_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccf580_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfbe0_0, 0;
|
||||||
|
%jmp T_2.1;
|
||||||
|
T_2.0 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08c000_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfb40_0, 0;
|
||||||
|
%load/vec4 v0x74b08c1e0_0;
|
||||||
|
%dup/vec4;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%cmp/u;
|
||||||
|
%jmp/1 T_2.2, 6;
|
||||||
|
%dup/vec4;
|
||||||
|
%pushi/vec4 1, 0, 2;
|
||||||
|
%cmp/u;
|
||||||
|
%jmp/1 T_2.3, 6;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%assign/vec4 v0x74b08c1e0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfb40_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccf580_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfbe0_0, 0;
|
||||||
|
%jmp T_2.5;
|
||||||
|
T_2.2 ;
|
||||||
|
%load/vec4 v0x74b08c0a0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.6, 8;
|
||||||
|
%load/vec4 v0x102ccb170_0;
|
||||||
|
%assign/vec4 v0x102ccd070_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfb40_0, 0;
|
||||||
|
%load/vec4 v0x74b08c320_0;
|
||||||
|
%assign/vec4 v0x102ccfd20_0, 0;
|
||||||
|
%load/vec4 v0x102ccb170_0;
|
||||||
|
%ix/load 4, 1, 0;
|
||||||
|
%flag_set/imm 4, 0;
|
||||||
|
%shiftr 4;
|
||||||
|
%assign/vec4 v0x102ccd820_0, 0;
|
||||||
|
%load/vec4 v0x102ccb170_0;
|
||||||
|
%parti/s 1, 0, 2;
|
||||||
|
%cmpi/e 0, 0, 1;
|
||||||
|
%jmp/0xz T_2.8, 4;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccf580_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfbe0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 8;
|
||||||
|
%load/vec4 v0x74b08c280_0;
|
||||||
|
%concat/vec4; draw_concat_vec4
|
||||||
|
%assign/vec4 v0x102ccfc80_0, 0;
|
||||||
|
%jmp T_2.9;
|
||||||
|
T_2.8 ;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccf580_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x102ccfbe0_0, 0;
|
||||||
|
%load/vec4 v0x74b08c280_0;
|
||||||
|
%concati/vec4 0, 0, 8;
|
||||||
|
%assign/vec4 v0x102ccfc80_0, 0;
|
||||||
|
T_2.9 ;
|
||||||
|
%pushi/vec4 1, 0, 2;
|
||||||
|
%assign/vec4 v0x74b08c1e0_0, 0;
|
||||||
|
T_2.6 ;
|
||||||
|
%jmp T_2.5;
|
||||||
|
T_2.3 ;
|
||||||
|
%load/vec4 v0x102ccfaa0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.10, 8;
|
||||||
|
%load/vec4 v0x102ccd070_0;
|
||||||
|
%parti/s 1, 0, 2;
|
||||||
|
%cmpi/e 0, 0, 1;
|
||||||
|
%jmp/0xz T_2.12, 4;
|
||||||
|
%load/vec4 v0x102ccfa00_0;
|
||||||
|
%parti/s 8, 0, 2;
|
||||||
|
%assign/vec4 v0x102ccfdc0_0, 0;
|
||||||
|
%jmp T_2.13;
|
||||||
|
T_2.12 ;
|
||||||
|
%load/vec4 v0x102ccfa00_0;
|
||||||
|
%parti/s 8, 8, 5;
|
||||||
|
%assign/vec4 v0x102ccfdc0_0, 0;
|
||||||
|
T_2.13 ;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08c000_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%assign/vec4 v0x74b08c1e0_0, 0;
|
||||||
|
T_2.10 ;
|
||||||
|
%jmp T_2.5;
|
||||||
|
T_2.5 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
T_2.1 ;
|
||||||
|
%jmp T_2;
|
||||||
|
.thread T_2;
|
||||||
|
.scope S_0x102cc09e0;
|
||||||
|
T_3 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x74b08c6e0_0, 0, 1;
|
||||||
|
T_3.0 ;
|
||||||
|
%delay 6250, 0;
|
||||||
|
%load/vec4 v0x74b08c6e0_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x74b08c6e0_0, 0, 1;
|
||||||
|
%jmp T_3.0;
|
||||||
|
T_3.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_3;
|
||||||
|
.scope S_0x102cc09e0;
|
||||||
|
T_4 ;
|
||||||
|
%vpi_call/w 3 99 "$dumpfile", "sim/int8_memory_access.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 100 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x102cc09e0 {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_4;
|
||||||
|
.scope S_0x102cc09e0;
|
||||||
|
T_5 ;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08ce60_0, 0;
|
||||||
|
%load/vec4 v0x74b08caa0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_5.0, 8;
|
||||||
|
%load/vec4 v0x74b08cc80_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_5.2, 8;
|
||||||
|
%load/vec4 v0x74b08c8c0_0;
|
||||||
|
%nor/r;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_5.4, 8;
|
||||||
|
%load/vec4 v0x74b08cbe0_0;
|
||||||
|
%parti/s 8, 0, 2;
|
||||||
|
%ix/getv 3, v0x74b08c820_0;
|
||||||
|
%ix/load 4, 0, 0; Constant delay
|
||||||
|
%assign/vec4/a/d v0x74b08cd20, 0, 4;
|
||||||
|
T_5.4 ;
|
||||||
|
%load/vec4 v0x74b08cb40_0;
|
||||||
|
%nor/r;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_5.6, 8;
|
||||||
|
%load/vec4 v0x74b08cbe0_0;
|
||||||
|
%parti/s 8, 8, 5;
|
||||||
|
%ix/getv 3, v0x74b08c820_0;
|
||||||
|
%ix/load 4, 8, 0; part off
|
||||||
|
%ix/load 5, 0, 0; Constant delay
|
||||||
|
%assign/vec4/a/d v0x74b08cd20, 4, 5;
|
||||||
|
T_5.6 ;
|
||||||
|
%jmp T_5.3;
|
||||||
|
T_5.2 ;
|
||||||
|
%ix/getv 4, v0x74b08c820_0;
|
||||||
|
%load/vec4a v0x74b08cd20, 4;
|
||||||
|
%assign/vec4 v0x74b08cdc0_0, 0;
|
||||||
|
T_5.3 ;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x74b08ce60_0, 0;
|
||||||
|
T_5.0 ;
|
||||||
|
%jmp T_5;
|
||||||
|
.thread T_5;
|
||||||
|
.scope S_0x102cc09e0;
|
||||||
|
T_6 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x74b08d040_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x74b08d220_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c640_0, 0, 22;
|
||||||
|
%pushi/vec4 0, 0, 8;
|
||||||
|
%store/vec4 v0x74b08d180_0, 0, 8;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%store/vec4 v0x74b08cdc0_0, 0, 16;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x74b08ce60_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x74b08c780_0, 0, 32;
|
||||||
|
T_6.0 ; Top of for-loop
|
||||||
|
%load/vec4 v0x74b08c780_0;
|
||||||
|
%cmpi/s 1024, 0, 32;
|
||||||
|
%jmp/0xz T_6.1, 5;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%ix/getv/s 4, v0x74b08c780_0;
|
||||||
|
%store/vec4a v0x74b08cd20, 4, 0;
|
||||||
|
T_6.2 ; for-loop step statement
|
||||||
|
%load/vec4 v0x74b08c780_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x74b08c780_0, 0, 32;
|
||||||
|
%jmp T_6.0;
|
||||||
|
T_6.1 ; for-loop exit label
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x74b08d0e0_0, 0, 1;
|
||||||
|
%pushi/vec4 5, 0, 32;
|
||||||
|
T_6.3 %dup/vec4;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%jmp/1xz T_6.4, 5;
|
||||||
|
%jmp/1 T_6.4, 4;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%wait E_0x74ac1b040;
|
||||||
|
%jmp T_6.3;
|
||||||
|
T_6.4 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x74b08d0e0_0, 0, 1;
|
||||||
|
%vpi_call/w 3 244 "$display", "\000" {0 0 0};
|
||||||
|
%vpi_call/w 3 245 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 246 "$display", "INT8 MEMORY ACCESS TEST" {0 0 0};
|
||||||
|
%vpi_call/w 3 247 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 248 "$display", "\000" {0 0 0};
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 18, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 1, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 52, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 2, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 86, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 3, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 120, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 18, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 1, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 52, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 2, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 86, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 3, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 120, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%ix/load 4, 0, 0;
|
||||||
|
%flag_set/imm 4, 0;
|
||||||
|
%load/vec4a v0x74b08cd20, 4;
|
||||||
|
%cmpi/ne 13330, 0, 16;
|
||||||
|
%jmp/0xz T_6.5, 6;
|
||||||
|
%vpi_call/w 3 276 "$display", "PACKING ERROR word[0]=0x%04x expected=0x3412", &A<v0x74b08cd20, 0> {0 0 0};
|
||||||
|
%vpi_call/w 3 281 "$fatal" {0 0 0};
|
||||||
|
%jmp T_6.6;
|
||||||
|
T_6.5 ;
|
||||||
|
%vpi_call/w 3 285 "$display", "PACKING word[0] = 0x%04x PASS", &A<v0x74b08cd20, 0> {0 0 0};
|
||||||
|
T_6.6 ;
|
||||||
|
%ix/load 4, 1, 0;
|
||||||
|
%flag_set/imm 4, 0;
|
||||||
|
%load/vec4a v0x74b08cd20, 4;
|
||||||
|
%cmpi/ne 30806, 0, 16;
|
||||||
|
%jmp/0xz T_6.7, 6;
|
||||||
|
%vpi_call/w 3 294 "$display", "PACKING ERROR word[1]=0x%04x expected=0x7856", &A<v0x74b08cd20, 1> {0 0 0};
|
||||||
|
%vpi_call/w 3 299 "$fatal" {0 0 0};
|
||||||
|
%jmp T_6.8;
|
||||||
|
T_6.7 ;
|
||||||
|
%vpi_call/w 3 303 "$display", "PACKING word[1] = 0x%04x PASS", &A<v0x74b08cd20, 1> {0 0 0};
|
||||||
|
T_6.8 ;
|
||||||
|
%pushi/vec4 16, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 52, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 17, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 18, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%ix/load 4, 8, 0;
|
||||||
|
%flag_set/imm 4, 0;
|
||||||
|
%load/vec4a v0x74b08cd20, 4;
|
||||||
|
%cmpi/ne 4660, 0, 16;
|
||||||
|
%jmp/0xz T_6.9, 6;
|
||||||
|
%vpi_call/w 3 320 "$display", "INITIAL WORD ERROR = 0x%04x", &A<v0x74b08cd20, 8> {0 0 0};
|
||||||
|
%vpi_call/w 3 325 "$fatal" {0 0 0};
|
||||||
|
T_6.9 ;
|
||||||
|
%pushi/vec4 16, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 170, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%ix/load 4, 8, 0;
|
||||||
|
%flag_set/imm 4, 0;
|
||||||
|
%load/vec4a v0x74b08cd20, 4;
|
||||||
|
%cmpi/ne 4778, 0, 16;
|
||||||
|
%jmp/0xz T_6.11, 6;
|
||||||
|
%vpi_call/w 3 336 "$display", "LOW BYTE PRESERVE ERROR = 0x%04x expected=0x12AA", &A<v0x74b08cd20, 8> {0 0 0};
|
||||||
|
%vpi_call/w 3 341 "$fatal" {0 0 0};
|
||||||
|
%jmp T_6.12;
|
||||||
|
T_6.11 ;
|
||||||
|
%vpi_call/w 3 345 "$display", "LOW BYTE PRESERVE 0x1234 -> 0x12AA PASS" {0 0 0};
|
||||||
|
T_6.12 ;
|
||||||
|
%pushi/vec4 17, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 187, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%ix/load 4, 8, 0;
|
||||||
|
%flag_set/imm 4, 0;
|
||||||
|
%load/vec4a v0x74b08cd20, 4;
|
||||||
|
%cmpi/ne 48042, 0, 16;
|
||||||
|
%jmp/0xz T_6.13, 6;
|
||||||
|
%vpi_call/w 3 358 "$display", "HIGH BYTE PRESERVE ERROR = 0x%04x expected=0xBBAA", &A<v0x74b08cd20, 8> {0 0 0};
|
||||||
|
%vpi_call/w 3 363 "$fatal" {0 0 0};
|
||||||
|
%jmp T_6.14;
|
||||||
|
T_6.13 ;
|
||||||
|
%vpi_call/w 3 367 "$display", "HIGH BYTE PRESERVE 0x12AA -> 0xBBAA PASS" {0 0 0};
|
||||||
|
T_6.14 ;
|
||||||
|
%pushi/vec4 32, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 255, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 33, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 128, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 34, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c500_0, 0, 22;
|
||||||
|
%pushi/vec4 127, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c5a0_0, 0, 8;
|
||||||
|
%fork TD_tb.write_byte, S_0x102cd1300;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 32, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 255, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 33, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 128, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 34, 0, 22;
|
||||||
|
%store/vec4 v0x74b08c3c0_0, 0, 22;
|
||||||
|
%pushi/vec4 127, 0, 8;
|
||||||
|
%store/vec4 v0x74b08c460_0, 0, 8;
|
||||||
|
%fork TD_tb.read_byte, S_0x102cd1180;
|
||||||
|
%join;
|
||||||
|
%vpi_call/w 3 389 "$display", "\000" {0 0 0};
|
||||||
|
%vpi_call/w 3 390 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 391 "$display", "INT8 MEMORY ACCESS TEST PASSED" {0 0 0};
|
||||||
|
%vpi_call/w 3 392 "$display", "BYTE ADDRESSING : PASS" {0 0 0};
|
||||||
|
%vpi_call/w 3 393 "$display", "LOW BYTE : PASS" {0 0 0};
|
||||||
|
%vpi_call/w 3 394 "$display", "HIGH BYTE : PASS" {0 0 0};
|
||||||
|
%vpi_call/w 3 395 "$display", "INT8 PACKING : PASS" {0 0 0};
|
||||||
|
%vpi_call/w 3 396 "$display", "BYTE PRESERVATION : PASS" {0 0 0};
|
||||||
|
%vpi_call/w 3 397 "$display", "SIGNED INT8 : PASS" {0 0 0};
|
||||||
|
%vpi_call/w 3 398 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 399 "$display", "\000" {0 0 0};
|
||||||
|
%vpi_call/w 3 401 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_6;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 5;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"sim/int8_memory_access_tb.v";
|
||||||
|
"rtl/int8_memory_access.v";
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb;
|
||||||
|
|
||||||
|
localparam ADDR_WIDTH = 22;
|
||||||
|
localparam CLK_PERIOD = 12.5;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// INT8 interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg req;
|
||||||
|
reg wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] addr;
|
||||||
|
reg signed [7:0] wdata;
|
||||||
|
|
||||||
|
wire signed [7:0] rdata;
|
||||||
|
wire ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Memory interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire mem_req;
|
||||||
|
wire mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||||
|
wire [15:0] mem_wdata;
|
||||||
|
wire mem_lb_n;
|
||||||
|
wire mem_ub_n;
|
||||||
|
|
||||||
|
wire [15:0] mem_rdata;
|
||||||
|
wire mem_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// DUT
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
int8_memory_access #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH)
|
||||||
|
) dut (
|
||||||
|
.clk (clk),
|
||||||
|
.rst (rst),
|
||||||
|
|
||||||
|
.req (req),
|
||||||
|
.wr (wr),
|
||||||
|
.addr (addr),
|
||||||
|
.wdata (wdata),
|
||||||
|
|
||||||
|
.rdata (rdata),
|
||||||
|
.ready (ready),
|
||||||
|
|
||||||
|
.mem_req (mem_req),
|
||||||
|
.mem_wr (mem_wr),
|
||||||
|
.mem_addr (mem_addr),
|
||||||
|
.mem_wdata (mem_wdata),
|
||||||
|
.mem_lb_n (mem_lb_n),
|
||||||
|
.mem_ub_n (mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata (mem_rdata),
|
||||||
|
.mem_ready (mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Simple memory model
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg [15:0] memory [0:1023];
|
||||||
|
|
||||||
|
reg [15:0] model_rdata;
|
||||||
|
reg model_ready;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
assign mem_rdata = model_rdata;
|
||||||
|
assign mem_ready = model_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Clock
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
clk = 1'b0;
|
||||||
|
|
||||||
|
forever #(CLK_PERIOD / 2.0)
|
||||||
|
clk = ~clk;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// VCD
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
$dumpfile("sim/int8_memory_access.vcd");
|
||||||
|
$dumpvars(0, tb);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Memory model
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
|
||||||
|
model_ready <= 1'b0;
|
||||||
|
|
||||||
|
if (mem_req) begin
|
||||||
|
|
||||||
|
if (mem_wr) begin
|
||||||
|
|
||||||
|
if (!mem_lb_n)
|
||||||
|
memory[mem_addr][7:0] <= mem_wdata[7:0];
|
||||||
|
|
||||||
|
if (!mem_ub_n)
|
||||||
|
memory[mem_addr][15:8] <= mem_wdata[15:8];
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
model_rdata <= memory[mem_addr];
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
model_ready <= 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Write helper
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task write_byte;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] byte_addr;
|
||||||
|
input signed [7:0] data;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
addr <= byte_addr;
|
||||||
|
wdata <= data;
|
||||||
|
wr <= 1'b1;
|
||||||
|
req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
req <= 1'b0;
|
||||||
|
|
||||||
|
wait (ready);
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"WRITE BYTE addr=0x%08x data=0x%02x PASS",
|
||||||
|
byte_addr,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Read helper
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task read_byte;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] byte_addr;
|
||||||
|
input signed [7:0] expected;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
addr <= byte_addr;
|
||||||
|
wr <= 1'b0;
|
||||||
|
req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
req <= 1'b0;
|
||||||
|
|
||||||
|
wait (ready);
|
||||||
|
|
||||||
|
if (rdata !== expected) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"READ BYTE addr=0x%08x FAIL got=0x%02x expected=0x%02x",
|
||||||
|
byte_addr,
|
||||||
|
rdata,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"READ BYTE addr=0x%08x data=0x%02x PASS",
|
||||||
|
byte_addr,
|
||||||
|
rdata
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Test
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
req = 1'b0;
|
||||||
|
wr = 1'b0;
|
||||||
|
addr = 0;
|
||||||
|
wdata = 0;
|
||||||
|
|
||||||
|
model_rdata = 16'h0000;
|
||||||
|
model_ready = 1'b0;
|
||||||
|
|
||||||
|
for (i = 0; i < 1024; i = i + 1)
|
||||||
|
memory[i] = 16'h0000;
|
||||||
|
|
||||||
|
rst = 1'b1;
|
||||||
|
|
||||||
|
repeat (5)
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
rst = 1'b0;
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("INT8 MEMORY ACCESS TEST");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Basic byte writes
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_byte(22'h000000, 8'h12);
|
||||||
|
write_byte(22'h000001, 8'h34);
|
||||||
|
|
||||||
|
write_byte(22'h000002, 8'h56);
|
||||||
|
write_byte(22'h000003, 8'h78);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Reads
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
read_byte(22'h000000, 8'h12);
|
||||||
|
read_byte(22'h000001, 8'h34);
|
||||||
|
|
||||||
|
read_byte(22'h000002, 8'h56);
|
||||||
|
read_byte(22'h000003, 8'h78);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Verify physical packing
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
if (memory[0] !== 16'h3412) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"PACKING ERROR word[0]=0x%04x expected=0x3412",
|
||||||
|
memory[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"PACKING word[0] = 0x%04x PASS",
|
||||||
|
memory[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if (memory[1] !== 16'h7856) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"PACKING ERROR word[1]=0x%04x expected=0x7856",
|
||||||
|
memory[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"PACKING word[1] = 0x%04x PASS",
|
||||||
|
memory[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Byte preservation test
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
// Start with 0x1234
|
||||||
|
write_byte(22'h000010, 8'h34);
|
||||||
|
write_byte(22'h000011, 8'h12);
|
||||||
|
|
||||||
|
if (memory[8] !== 16'h1234) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"INITIAL WORD ERROR = 0x%04x",
|
||||||
|
memory[8]
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// Change low byte only:
|
||||||
|
// 0x1234 -> 0x12AA
|
||||||
|
|
||||||
|
write_byte(22'h000010, 8'hAA);
|
||||||
|
|
||||||
|
if (memory[8] !== 16'h12AA) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"LOW BYTE PRESERVE ERROR = 0x%04x expected=0x12AA",
|
||||||
|
memory[8]
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"LOW BYTE PRESERVE 0x1234 -> 0x12AA PASS"
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// Change high byte only:
|
||||||
|
// 0x12AA -> 0xBBAA
|
||||||
|
|
||||||
|
write_byte(22'h000011, 8'hBB);
|
||||||
|
|
||||||
|
if (memory[8] !== 16'hBBAA) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"HIGH BYTE PRESERVE ERROR = 0x%04x expected=0xBBAA",
|
||||||
|
memory[8]
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"HIGH BYTE PRESERVE 0x12AA -> 0xBBAA PASS"
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Signed INT8 values
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_byte(22'h000020, -8'sd1);
|
||||||
|
write_byte(22'h000021, -8'sd128);
|
||||||
|
write_byte(22'h000022, 8'sd127);
|
||||||
|
|
||||||
|
read_byte(22'h000020, -8'sd1);
|
||||||
|
read_byte(22'h000021, -8'sd128);
|
||||||
|
read_byte(22'h000022, 8'sd127);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Final result
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("INT8 MEMORY ACCESS TEST PASSED");
|
||||||
|
$display("BYTE ADDRESSING : PASS");
|
||||||
|
$display("LOW BYTE : PASS");
|
||||||
|
$display("HIGH BYTE : PASS");
|
||||||
|
$display("INT8 PACKING : PASS");
|
||||||
|
$display("BYTE PRESERVATION : PASS");
|
||||||
|
$display("SIGNED INT8 : PASS");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
File diff suppressed because it is too large
Load Diff
Executable
+22197
File diff suppressed because one or more lines are too long
@@ -0,0 +1,455 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb;
|
||||||
|
|
||||||
|
localparam ADDR_WIDTH = 22;
|
||||||
|
localparam DATA_WIDTH = 16;
|
||||||
|
localparam CLK_PERIOD = 12.5; // 80 MHz
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Clock / Reset
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// INT8 interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg req;
|
||||||
|
reg wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] addr;
|
||||||
|
reg signed [7:0] wdata;
|
||||||
|
|
||||||
|
wire signed [7:0] rdata;
|
||||||
|
wire ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// memory_interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire mem_req;
|
||||||
|
wire mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||||
|
wire [DATA_WIDTH-1:0] mem_wdata;
|
||||||
|
wire mem_lb_n;
|
||||||
|
wire mem_ub_n;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] mem_rdata;
|
||||||
|
wire mem_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Memory interface -> PSRAM controller
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire psram_mem_req;
|
||||||
|
wire psram_mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] psram_mem_addr;
|
||||||
|
wire [DATA_WIDTH-1:0] psram_mem_wdata;
|
||||||
|
wire psram_mem_lb_n;
|
||||||
|
wire psram_mem_ub_n;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] psram_mem_rdata;
|
||||||
|
wire psram_mem_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM physical interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire [ADDR_WIDTH-1:0] psram_a;
|
||||||
|
wire [DATA_WIDTH-1:0] psram_dq;
|
||||||
|
|
||||||
|
wire psram_ce_n;
|
||||||
|
wire psram_oe_n;
|
||||||
|
wire psram_we_n;
|
||||||
|
wire psram_lb_n;
|
||||||
|
wire psram_ub_n;
|
||||||
|
wire psram_zz_n;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// INT8 MEMORY ACCESS
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
int8_memory_access #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH)
|
||||||
|
) int8_access (
|
||||||
|
.clk (clk),
|
||||||
|
.rst (rst),
|
||||||
|
|
||||||
|
.req (req),
|
||||||
|
.wr (wr),
|
||||||
|
.addr (addr),
|
||||||
|
.wdata (wdata),
|
||||||
|
|
||||||
|
.rdata (rdata),
|
||||||
|
.ready (ready),
|
||||||
|
|
||||||
|
.mem_req (mem_req),
|
||||||
|
.mem_wr (mem_wr),
|
||||||
|
.mem_addr (mem_addr),
|
||||||
|
.mem_wdata (mem_wdata),
|
||||||
|
.mem_lb_n (mem_lb_n),
|
||||||
|
.mem_ub_n (mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata (mem_rdata),
|
||||||
|
.mem_ready (mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// MEMORY INTERFACE
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
memory_interface #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH)
|
||||||
|
) memory_if (
|
||||||
|
.clk (clk),
|
||||||
|
.rst (rst),
|
||||||
|
|
||||||
|
.req (mem_req),
|
||||||
|
.wr (mem_wr),
|
||||||
|
.addr (mem_addr),
|
||||||
|
.wdata (mem_wdata),
|
||||||
|
.lb_n (mem_lb_n),
|
||||||
|
.ub_n (mem_ub_n),
|
||||||
|
|
||||||
|
.rdata (mem_rdata),
|
||||||
|
.ready (mem_ready),
|
||||||
|
|
||||||
|
.mem_req (psram_mem_req),
|
||||||
|
.mem_wr (psram_mem_wr),
|
||||||
|
.mem_addr (psram_mem_addr),
|
||||||
|
.mem_wdata (psram_mem_wdata),
|
||||||
|
.mem_lb_n (psram_mem_lb_n),
|
||||||
|
.mem_ub_n (psram_mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata (psram_mem_rdata),
|
||||||
|
.mem_ready (psram_mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM CONTROLLER
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
psram_controller #(
|
||||||
|
.ADDR_WIDTH (ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH (DATA_WIDTH),
|
||||||
|
.CLK_FREQ_MHZ (80)
|
||||||
|
) psram_ctrl (
|
||||||
|
.clk (clk),
|
||||||
|
.rst (rst),
|
||||||
|
|
||||||
|
.mem_req (psram_mem_req),
|
||||||
|
.mem_wr (psram_mem_wr),
|
||||||
|
.mem_addr (psram_mem_addr),
|
||||||
|
.mem_wdata (psram_mem_wdata),
|
||||||
|
.mem_lb_n (psram_mem_lb_n),
|
||||||
|
.mem_ub_n (psram_mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata (psram_mem_rdata),
|
||||||
|
.mem_ready (psram_mem_ready),
|
||||||
|
|
||||||
|
.psram_a (psram_a),
|
||||||
|
.psram_dq (psram_dq),
|
||||||
|
|
||||||
|
.psram_ce_n(psram_ce_n),
|
||||||
|
.psram_oe_n(psram_oe_n),
|
||||||
|
.psram_we_n(psram_we_n),
|
||||||
|
.psram_lb_n(psram_lb_n),
|
||||||
|
.psram_ub_n(psram_ub_n),
|
||||||
|
.psram_zz_n(psram_zz_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM MODEL
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
psram_model #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.DEPTH(16384)
|
||||||
|
) psram (
|
||||||
|
.clk (clk),
|
||||||
|
.a (psram_a),
|
||||||
|
.dq (psram_dq),
|
||||||
|
.ce_n (psram_ce_n),
|
||||||
|
.oe_n (psram_oe_n),
|
||||||
|
.we_n (psram_we_n),
|
||||||
|
.lb_n (psram_lb_n),
|
||||||
|
.ub_n (psram_ub_n),
|
||||||
|
.zz_n (psram_zz_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Clock
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
clk = 1'b0;
|
||||||
|
|
||||||
|
forever #(CLK_PERIOD / 2.0)
|
||||||
|
clk = ~clk;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// VCD
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
$dumpfile("sim/int8_psram_integration.vcd");
|
||||||
|
$dumpvars(0, tb);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Write INT8
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task write_byte;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] byte_addr;
|
||||||
|
input signed [7:0] data;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
addr <= byte_addr;
|
||||||
|
wdata <= data;
|
||||||
|
wr <= 1'b1;
|
||||||
|
req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
req <= 1'b0;
|
||||||
|
|
||||||
|
wait (ready);
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"WRITE BYTE addr=0x%08x data=0x%02x PASS",
|
||||||
|
byte_addr,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Read INT8
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task read_byte;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] byte_addr;
|
||||||
|
input signed [7:0] expected;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
addr <= byte_addr;
|
||||||
|
wr <= 1'b0;
|
||||||
|
req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
req <= 1'b0;
|
||||||
|
|
||||||
|
wait (ready);
|
||||||
|
|
||||||
|
if (rdata !== expected) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"READ BYTE addr=0x%08x FAIL got=0x%02x expected=0x%02x",
|
||||||
|
byte_addr,
|
||||||
|
rdata,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"READ BYTE addr=0x%08x data=0x%02x PASS",
|
||||||
|
byte_addr,
|
||||||
|
rdata
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Test
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
integer test_addr;
|
||||||
|
reg signed [7:0] test_data;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
req = 1'b0;
|
||||||
|
wr = 1'b0;
|
||||||
|
addr = 0;
|
||||||
|
wdata = 0;
|
||||||
|
|
||||||
|
rst = 1'b1;
|
||||||
|
|
||||||
|
repeat (5)
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
rst = 1'b0;
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("INT8 + PSRAM FULL INTEGRATION TEST");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Wait for PSRAM initialization
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
wait (psram_ctrl.state == psram_ctrl.STATE_IDLE);
|
||||||
|
|
||||||
|
$display("PSRAM initialization complete");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// BASIC INT8 PACKING
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_byte(22'h000000, 8'h12);
|
||||||
|
write_byte(22'h000001, 8'h34);
|
||||||
|
|
||||||
|
write_byte(22'h000002, 8'h56);
|
||||||
|
write_byte(22'h000003, 8'h78);
|
||||||
|
|
||||||
|
read_byte(22'h000000, 8'h12);
|
||||||
|
read_byte(22'h000001, 8'h34);
|
||||||
|
read_byte(22'h000002, 8'h56);
|
||||||
|
read_byte(22'h000003, 8'h78);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// BYTE PRESERVATION
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_byte(22'h000010, 8'h34);
|
||||||
|
write_byte(22'h000011, 8'h12);
|
||||||
|
|
||||||
|
write_byte(22'h000010, 8'hAA);
|
||||||
|
|
||||||
|
read_byte(22'h000010, 8'hAA);
|
||||||
|
read_byte(22'h000011, 8'h12);
|
||||||
|
|
||||||
|
write_byte(22'h000011, 8'hBB);
|
||||||
|
|
||||||
|
read_byte(22'h000010, 8'hAA);
|
||||||
|
read_byte(22'h000011, 8'hBB);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// SIGNED INT8
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_byte(22'h000020, -8'sd1);
|
||||||
|
write_byte(22'h000021, -8'sd128);
|
||||||
|
write_byte(22'h000022, 8'sd127);
|
||||||
|
|
||||||
|
read_byte(22'h000020, -8'sd1);
|
||||||
|
read_byte(22'h000021, -8'sd128);
|
||||||
|
read_byte(22'h000022, 8'sd127);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// SPARSE ADDRESSES
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_byte(22'h000100, 8'h11);
|
||||||
|
write_byte(22'h000101, 8'h22);
|
||||||
|
|
||||||
|
write_byte(22'h001000, 8'h33);
|
||||||
|
write_byte(22'h001001, 8'h44);
|
||||||
|
|
||||||
|
write_byte(22'h003FFE, 8'h55);
|
||||||
|
write_byte(22'h003FFF, 8'h66);
|
||||||
|
|
||||||
|
read_byte(22'h000100, 8'h11);
|
||||||
|
read_byte(22'h000101, 8'h22);
|
||||||
|
|
||||||
|
read_byte(22'h001000, 8'h33);
|
||||||
|
read_byte(22'h001001, 8'h44);
|
||||||
|
|
||||||
|
read_byte(22'h003FFE, 8'h55);
|
||||||
|
read_byte(22'h003FFF, 8'h66);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// STRESS TEST
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("INT8 PSRAM STRESS TEST");
|
||||||
|
$display("2048 WRITE + READ BYTE TRANSACTIONS");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
for (i = 0; i < 2048; i = i + 1) begin
|
||||||
|
|
||||||
|
test_addr =
|
||||||
|
((i * 7919) ^ (i << 5)) & 16'h7FFF;
|
||||||
|
|
||||||
|
test_data =
|
||||||
|
((i * 1237) ^ 8'hA5);
|
||||||
|
|
||||||
|
write_byte(
|
||||||
|
test_addr,
|
||||||
|
test_data
|
||||||
|
);
|
||||||
|
|
||||||
|
read_byte(
|
||||||
|
test_addr,
|
||||||
|
test_data
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((i % 128) == 0)
|
||||||
|
$display(
|
||||||
|
"STRESS %0d / 2048 PASS",
|
||||||
|
i
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Final result
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("INT8 + PSRAM INTEGRATION TEST PASSED");
|
||||||
|
$display("BYTE ADDRESSING : PASS");
|
||||||
|
$display("LB# / UB# : PASS");
|
||||||
|
$display("INT8 PACKING : PASS");
|
||||||
|
$display("BYTE PRESERVATION : PASS");
|
||||||
|
$display("SIGNED INT8 : PASS");
|
||||||
|
$display("SPARSE ADDRESSES : PASS");
|
||||||
|
$display("2048 STRESS : PASS");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
File diff suppressed because it is too large
Load Diff
Executable
+504
@@ -0,0 +1,504 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x1008dc9a0 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x1008dbb60 .scope module, "memory_interface_tb" "memory_interface_tb" 3 3;
|
||||||
|
.timescale -9 -12;
|
||||||
|
P_0x1008dbce0 .param/l "ADDR_WIDTH" 0 3 5, +C4<00000000000000000000000000010110>;
|
||||||
|
P_0x1008dbd20 .param/l "DATA_WIDTH" 0 3 6, +C4<00000000000000000000000000010000>;
|
||||||
|
v0x83f094e60_0 .var "addr", 21 0;
|
||||||
|
v0x83f094f00_0 .var "clk", 0 0;
|
||||||
|
v0x83f094fa0_0 .net "mem_addr", 21 0, v0x1008cea80_0; 1 drivers
|
||||||
|
v0x83f095040_0 .net "mem_rdata", 15 0, v0x83f094780_0; 1 drivers
|
||||||
|
v0x83f0950e0_0 .net "mem_ready", 0 0, v0x83f094820_0; 1 drivers
|
||||||
|
v0x83f095180_0 .net "mem_req", 0 0, v0x1008d8580_0; 1 drivers
|
||||||
|
v0x83f095220_0 .net "mem_wdata", 15 0, v0x1008d8620_0; 1 drivers
|
||||||
|
v0x83f0952c0_0 .net "mem_wr", 0 0, v0x1008d86c0_0; 1 drivers
|
||||||
|
v0x83f095360_0 .net "rdata", 15 0, v0x1008d8760_0; 1 drivers
|
||||||
|
v0x83f095400_0 .net "ready", 0 0, v0x1008d8800_0; 1 drivers
|
||||||
|
v0x83f0954a0_0 .var "req", 0 0;
|
||||||
|
v0x83f095540_0 .var "rst", 0 0;
|
||||||
|
v0x83f0955e0_0 .var "wdata", 15 0;
|
||||||
|
v0x83f095680_0 .var "wr", 0 0;
|
||||||
|
S_0x1008d97e0 .scope module, "dut" "memory_interface" 3 46, 4 1 0, S_0x1008dbb60;
|
||||||
|
.timescale -9 -12;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
.port_info 2 /INPUT 1 "req";
|
||||||
|
.port_info 3 /INPUT 1 "wr";
|
||||||
|
.port_info 4 /INPUT 22 "addr";
|
||||||
|
.port_info 5 /INPUT 16 "wdata";
|
||||||
|
.port_info 6 /OUTPUT 16 "rdata";
|
||||||
|
.port_info 7 /OUTPUT 1 "ready";
|
||||||
|
.port_info 8 /OUTPUT 1 "mem_req";
|
||||||
|
.port_info 9 /OUTPUT 1 "mem_wr";
|
||||||
|
.port_info 10 /OUTPUT 22 "mem_addr";
|
||||||
|
.port_info 11 /OUTPUT 16 "mem_wdata";
|
||||||
|
.port_info 12 /INPUT 16 "mem_rdata";
|
||||||
|
.port_info 13 /INPUT 1 "mem_ready";
|
||||||
|
P_0x1008dcb20 .param/l "ADDR_WIDTH" 0 4 2, +C4<00000000000000000000000000010110>;
|
||||||
|
P_0x1008dcb60 .param/l "DATA_WIDTH" 0 4 3, +C4<00000000000000000000000000010000>;
|
||||||
|
P_0x1008dcba0 .param/l "STATE_IDLE" 1 4 27, C4<00>;
|
||||||
|
P_0x1008dcbe0 .param/l "STATE_WAIT" 1 4 28, C4<01>;
|
||||||
|
v0x1008ce940_0 .net "addr", 21 0, v0x83f094e60_0; 1 drivers
|
||||||
|
v0x1008ce9e0_0 .net "clk", 0 0, v0x83f094f00_0; 1 drivers
|
||||||
|
v0x1008cea80_0 .var "mem_addr", 21 0;
|
||||||
|
v0x1008ceb20_0 .net "mem_rdata", 15 0, v0x83f094780_0; alias, 1 drivers
|
||||||
|
v0x1008cebc0_0 .net "mem_ready", 0 0, v0x83f094820_0; alias, 1 drivers
|
||||||
|
v0x1008d8580_0 .var "mem_req", 0 0;
|
||||||
|
v0x1008d8620_0 .var "mem_wdata", 15 0;
|
||||||
|
v0x1008d86c0_0 .var "mem_wr", 0 0;
|
||||||
|
v0x1008d8760_0 .var "rdata", 15 0;
|
||||||
|
v0x1008d8800_0 .var "ready", 0 0;
|
||||||
|
v0x1008de100_0 .net "req", 0 0, v0x83f0954a0_0; 1 drivers
|
||||||
|
v0x1008de1a0_0 .net "rst", 0 0, v0x83f095540_0; 1 drivers
|
||||||
|
v0x83f094000_0 .var "state", 1 0;
|
||||||
|
v0x83f0940a0_0 .net "wdata", 15 0, v0x83f0955e0_0; 1 drivers
|
||||||
|
v0x83f094140_0 .net "wr", 0 0, v0x83f095680_0; 1 drivers
|
||||||
|
E_0x83ec14080 .event posedge, v0x1008ce9e0_0;
|
||||||
|
S_0x1008de240 .scope module, "memory" "memory_model" 3 76, 5 1 0, S_0x1008dbb60;
|
||||||
|
.timescale -9 -12;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
.port_info 2 /INPUT 1 "req";
|
||||||
|
.port_info 3 /INPUT 1 "wr";
|
||||||
|
.port_info 4 /INPUT 22 "addr";
|
||||||
|
.port_info 5 /INPUT 16 "wdata";
|
||||||
|
.port_info 6 /OUTPUT 16 "rdata";
|
||||||
|
.port_info 7 /OUTPUT 1 "ready";
|
||||||
|
P_0x1008de3c0 .param/l "ADDR_WIDTH" 0 5 2, +C4<00000000000000000000000000010110>;
|
||||||
|
P_0x1008de400 .param/l "DATA_WIDTH" 0 5 3, +C4<00000000000000000000000000010000>;
|
||||||
|
P_0x1008de440 .param/l "DEPTH" 0 5 4, +C4<00000000000000000001000000000000>;
|
||||||
|
P_0x1008de480 .param/l "READ_LATENCY" 0 5 5, +C4<00000000000000000000000000000010>;
|
||||||
|
v0x83f0941e0_0 .net "addr", 21 0, v0x1008cea80_0; alias, 1 drivers
|
||||||
|
v0x83f094280_0 .var "busy", 0 0;
|
||||||
|
v0x83f094320_0 .net "clk", 0 0, v0x83f094f00_0; alias, 1 drivers
|
||||||
|
v0x83f0943c0_0 .var/i "delay_count", 31 0;
|
||||||
|
v0x83f094460_0 .var/i "i", 31 0;
|
||||||
|
v0x83f094500 .array "mem", 4095 0, 15 0;
|
||||||
|
v0x83f0945a0_0 .var "pending_addr", 21 0;
|
||||||
|
v0x83f094640_0 .var "pending_wdata", 15 0;
|
||||||
|
v0x83f0946e0_0 .var "pending_wr", 0 0;
|
||||||
|
v0x83f094780_0 .var "rdata", 15 0;
|
||||||
|
v0x83f094820_0 .var "ready", 0 0;
|
||||||
|
v0x83f0948c0_0 .net "req", 0 0, v0x1008d8580_0; alias, 1 drivers
|
||||||
|
v0x83f094960_0 .net "rst", 0 0, v0x83f095540_0; alias, 1 drivers
|
||||||
|
v0x83f094a00_0 .net "wdata", 15 0, v0x1008d8620_0; alias, 1 drivers
|
||||||
|
v0x83f094aa0_0 .net "wr", 0 0, v0x1008d86c0_0; alias, 1 drivers
|
||||||
|
S_0x1008ded00 .scope task, "read_mem" "read_mem" 3 120, 3 120 0, S_0x1008dbb60;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x83f094b40_0 .var "address", 21 0;
|
||||||
|
v0x83f094be0_0 .var "expected", 15 0;
|
||||||
|
v0x83f094c80_0 .var "received", 15 0;
|
||||||
|
E_0x83ec17000 .event anyedge, v0x1008d8800_0;
|
||||||
|
TD_memory_interface_tb.read_mem ;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%load/vec4 v0x83f094b40_0;
|
||||||
|
%assign/vec4 v0x83f094e60_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f095680_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x83f0954a0_0, 0;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f0954a0_0, 0;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x83f095400_0;
|
||||||
|
%cmpi/ne 1, 0, 1;
|
||||||
|
%jmp/0xz T_0.1, 6;
|
||||||
|
%wait E_0x83ec17000;
|
||||||
|
%jmp T_0.0;
|
||||||
|
T_0.1 ;
|
||||||
|
%load/vec4 v0x83f095360_0;
|
||||||
|
%store/vec4 v0x83f094c80_0, 0, 16;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%load/vec4 v0x83f094c80_0;
|
||||||
|
%load/vec4 v0x83f094be0_0;
|
||||||
|
%cmp/e;
|
||||||
|
%jmp/0xz T_0.2, 6;
|
||||||
|
%vpi_call/w 3 144 "$display", "READ addr=0x%08h data=0x%04h PASS", v0x83f094b40_0, v0x83f094c80_0 {0 0 0};
|
||||||
|
%jmp T_0.3;
|
||||||
|
T_0.2 ;
|
||||||
|
%vpi_call/w 3 150 "$display", "READ addr=0x%08h got=0x%04h expected=0x%04h FAIL", v0x83f094b40_0, v0x83f094c80_0, v0x83f094be0_0 {0 0 0};
|
||||||
|
%vpi_call/w 3 157 "$fatal" {0 0 0};
|
||||||
|
T_0.3 ;
|
||||||
|
%end;
|
||||||
|
S_0x1008dee80 .scope task, "write_mem" "write_mem" 3 93, 3 93 0, S_0x1008dbb60;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x83f094d20_0 .var "address", 21 0;
|
||||||
|
v0x83f094dc0_0 .var "data", 15 0;
|
||||||
|
TD_memory_interface_tb.write_mem ;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%load/vec4 v0x83f094d20_0;
|
||||||
|
%assign/vec4 v0x83f094e60_0, 0;
|
||||||
|
%load/vec4 v0x83f094dc0_0;
|
||||||
|
%assign/vec4 v0x83f0955e0_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x83f095680_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x83f0954a0_0, 0;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f0954a0_0, 0;
|
||||||
|
T_1.4 ;
|
||||||
|
%load/vec4 v0x83f095400_0;
|
||||||
|
%cmpi/ne 1, 0, 1;
|
||||||
|
%jmp/0xz T_1.5, 6;
|
||||||
|
%wait E_0x83ec17000;
|
||||||
|
%jmp T_1.4;
|
||||||
|
T_1.5 ;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%vpi_call/w 3 112 "$display", "WRITE addr=0x%08h data=0x%04h PASS", v0x83f094d20_0, v0x83f094dc0_0 {0 0 0};
|
||||||
|
%end;
|
||||||
|
.scope S_0x1008d97e0;
|
||||||
|
T_2 ;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%load/vec4 v0x1008de1a0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%assign/vec4 v0x83f094000_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%assign/vec4 v0x1008d8760_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d8800_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d8580_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d86c0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%assign/vec4 v0x1008cea80_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%assign/vec4 v0x1008d8620_0, 0;
|
||||||
|
%jmp T_2.1;
|
||||||
|
T_2.0 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d8800_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d8580_0, 0;
|
||||||
|
%load/vec4 v0x83f094000_0;
|
||||||
|
%dup/vec4;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%cmp/u;
|
||||||
|
%jmp/1 T_2.2, 6;
|
||||||
|
%dup/vec4;
|
||||||
|
%pushi/vec4 1, 0, 2;
|
||||||
|
%cmp/u;
|
||||||
|
%jmp/1 T_2.3, 6;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%assign/vec4 v0x83f094000_0, 0;
|
||||||
|
%jmp T_2.5;
|
||||||
|
T_2.2 ;
|
||||||
|
%load/vec4 v0x1008de100_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.6, 8;
|
||||||
|
%load/vec4 v0x83f094140_0;
|
||||||
|
%assign/vec4 v0x1008d86c0_0, 0;
|
||||||
|
%load/vec4 v0x1008ce940_0;
|
||||||
|
%assign/vec4 v0x1008cea80_0, 0;
|
||||||
|
%load/vec4 v0x83f0940a0_0;
|
||||||
|
%assign/vec4 v0x1008d8620_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d8580_0, 0;
|
||||||
|
%pushi/vec4 1, 0, 2;
|
||||||
|
%assign/vec4 v0x83f094000_0, 0;
|
||||||
|
T_2.6 ;
|
||||||
|
%jmp T_2.5;
|
||||||
|
T_2.3 ;
|
||||||
|
%load/vec4 v0x1008cebc0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.8, 8;
|
||||||
|
%load/vec4 v0x1008d86c0_0;
|
||||||
|
%nor/r;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_2.10, 8;
|
||||||
|
%load/vec4 v0x1008ceb20_0;
|
||||||
|
%assign/vec4 v0x1008d8760_0, 0;
|
||||||
|
T_2.10 ;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x1008d8800_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 2;
|
||||||
|
%assign/vec4 v0x83f094000_0, 0;
|
||||||
|
T_2.8 ;
|
||||||
|
%jmp T_2.5;
|
||||||
|
T_2.5 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
T_2.1 ;
|
||||||
|
%jmp T_2;
|
||||||
|
.thread T_2;
|
||||||
|
.scope S_0x1008de240;
|
||||||
|
T_3 ;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%load/vec4 v0x83f094960_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_3.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%assign/vec4 v0x83f094780_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f094820_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f094280_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f0946e0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%assign/vec4 v0x83f0945a0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%assign/vec4 v0x83f094640_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%assign/vec4 v0x83f0943c0_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x83f094460_0, 0, 32;
|
||||||
|
T_3.2 ; Top of for-loop
|
||||||
|
%load/vec4 v0x83f094460_0;
|
||||||
|
%cmpi/s 4096, 0, 32;
|
||||||
|
%jmp/0xz T_3.3, 5;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%ix/getv/s 3, v0x83f094460_0;
|
||||||
|
%ix/load 4, 0, 0; Constant delay
|
||||||
|
%assign/vec4/a/d v0x83f094500, 0, 4;
|
||||||
|
T_3.4 ; for-loop step statement
|
||||||
|
%load/vec4 v0x83f094460_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x83f094460_0, 0, 32;
|
||||||
|
%jmp T_3.2;
|
||||||
|
T_3.3 ; for-loop exit label
|
||||||
|
%jmp T_3.1;
|
||||||
|
T_3.0 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f094820_0, 0;
|
||||||
|
%load/vec4 v0x83f094280_0;
|
||||||
|
%nor/r;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_3.5, 8;
|
||||||
|
%load/vec4 v0x83f0948c0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_3.7, 8;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x83f094280_0, 0;
|
||||||
|
%load/vec4 v0x83f094aa0_0;
|
||||||
|
%assign/vec4 v0x83f0946e0_0, 0;
|
||||||
|
%load/vec4 v0x83f0941e0_0;
|
||||||
|
%assign/vec4 v0x83f0945a0_0, 0;
|
||||||
|
%load/vec4 v0x83f094a00_0;
|
||||||
|
%assign/vec4 v0x83f094640_0, 0;
|
||||||
|
%pushi/vec4 2, 0, 32;
|
||||||
|
%assign/vec4 v0x83f0943c0_0, 0;
|
||||||
|
T_3.7 ;
|
||||||
|
%jmp T_3.6;
|
||||||
|
T_3.5 ;
|
||||||
|
%load/vec4 v0x83f0943c0_0;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%flag_or 5, 4; GT is !LE
|
||||||
|
%flag_inv 5;
|
||||||
|
%jmp/0xz T_3.9, 5;
|
||||||
|
%load/vec4 v0x83f0943c0_0;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%assign/vec4 v0x83f0943c0_0, 0;
|
||||||
|
%jmp T_3.10;
|
||||||
|
T_3.9 ;
|
||||||
|
%load/vec4 v0x83f0946e0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_3.11, 8;
|
||||||
|
%load/vec4 v0x83f0945a0_0;
|
||||||
|
%pad/u 32;
|
||||||
|
%cmpi/u 4096, 0, 32;
|
||||||
|
%jmp/0xz T_3.13, 5;
|
||||||
|
%load/vec4 v0x83f094640_0;
|
||||||
|
%ix/getv 3, v0x83f0945a0_0;
|
||||||
|
%ix/load 4, 0, 0; Constant delay
|
||||||
|
%assign/vec4/a/d v0x83f094500, 0, 4;
|
||||||
|
T_3.13 ;
|
||||||
|
%jmp T_3.12;
|
||||||
|
T_3.11 ;
|
||||||
|
%load/vec4 v0x83f0945a0_0;
|
||||||
|
%pad/u 32;
|
||||||
|
%cmpi/u 4096, 0, 32;
|
||||||
|
%jmp/0xz T_3.15, 5;
|
||||||
|
%ix/getv 4, v0x83f0945a0_0;
|
||||||
|
%load/vec4a v0x83f094500, 4;
|
||||||
|
%assign/vec4 v0x83f094780_0, 0;
|
||||||
|
%jmp T_3.16;
|
||||||
|
T_3.15 ;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%assign/vec4 v0x83f094780_0, 0;
|
||||||
|
T_3.16 ;
|
||||||
|
T_3.12 ;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%assign/vec4 v0x83f094820_0, 0;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%assign/vec4 v0x83f094280_0, 0;
|
||||||
|
T_3.10 ;
|
||||||
|
T_3.6 ;
|
||||||
|
T_3.1 ;
|
||||||
|
%jmp T_3;
|
||||||
|
.thread T_3;
|
||||||
|
.scope S_0x1008dbb60;
|
||||||
|
T_4 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x83f094f00_0, 0, 1;
|
||||||
|
T_4.0 ;
|
||||||
|
%delay 5000, 0;
|
||||||
|
%load/vec4 v0x83f094f00_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x83f094f00_0, 0, 1;
|
||||||
|
%jmp T_4.0;
|
||||||
|
T_4.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_4;
|
||||||
|
.scope S_0x1008dbb60;
|
||||||
|
T_5 ;
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x83f095540_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x83f0954a0_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x83f095680_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 22;
|
||||||
|
%store/vec4 v0x83f094e60_0, 0, 22;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%store/vec4 v0x83f0955e0_0, 0, 16;
|
||||||
|
%pushi/vec4 3, 0, 32;
|
||||||
|
T_5.0 %dup/vec4;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%jmp/1xz T_5.1, 5;
|
||||||
|
%jmp/1 T_5.1, 4;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%wait E_0x83ec14080;
|
||||||
|
%jmp T_5.0;
|
||||||
|
T_5.1 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x83f095540_0, 0, 1;
|
||||||
|
%vpi_call/w 3 182 "$display", "\000" {0 0 0};
|
||||||
|
%vpi_call/w 3 183 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 184 "$display", "MEMORY INTERFACE V1 TEST" {0 0 0};
|
||||||
|
%vpi_call/w 3 185 "$display", "ADDR_WIDTH = %0d", P_0x1008dbce0 {0 0 0};
|
||||||
|
%vpi_call/w 3 186 "$display", "DATA_WIDTH = %0d", P_0x1008dbd20 {0 0 0};
|
||||||
|
%vpi_call/w 3 187 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 188 "$display", "\000" {0 0 0};
|
||||||
|
%pushi/vec4 1, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 4660, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 1, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 4660, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 16, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 43981, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 16, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 43981, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 256, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 21930, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 256, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 21930, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 512, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 1, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 513, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 2, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 514, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 3, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 512, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 1, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 513, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 2, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 514, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 3, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 768, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 768, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 0, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 769, 0, 22;
|
||||||
|
%store/vec4 v0x83f094d20_0, 0, 22;
|
||||||
|
%pushi/vec4 65535, 0, 16;
|
||||||
|
%store/vec4 v0x83f094dc0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.write_mem, S_0x1008dee80;
|
||||||
|
%join;
|
||||||
|
%pushi/vec4 769, 0, 22;
|
||||||
|
%store/vec4 v0x83f094b40_0, 0, 22;
|
||||||
|
%pushi/vec4 65535, 0, 16;
|
||||||
|
%store/vec4 v0x83f094be0_0, 0, 16;
|
||||||
|
%fork TD_memory_interface_tb.read_mem, S_0x1008ded00;
|
||||||
|
%join;
|
||||||
|
%vpi_call/w 3 301 "$display", "\000" {0 0 0};
|
||||||
|
%vpi_call/w 3 302 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 303 "$display", "MEMORY INTERFACE V1 TEST PASSED" {0 0 0};
|
||||||
|
%vpi_call/w 3 304 "$display", "========================================" {0 0 0};
|
||||||
|
%vpi_call/w 3 305 "$display", "\000" {0 0 0};
|
||||||
|
%vpi_call/w 3 307 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_5;
|
||||||
|
.scope S_0x1008dbb60;
|
||||||
|
T_6 ;
|
||||||
|
%vpi_call/w 3 315 "$dumpfile", "sim/memory_interface.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 316 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x1008dbb60 {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_6;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 6;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"sim/memory_interface_tb.v";
|
||||||
|
"rtl/memory_interface.v";
|
||||||
|
"rtl/memory_model.v";
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module memory_interface_tb;
|
||||||
|
|
||||||
|
parameter ADDR_WIDTH = 22;
|
||||||
|
parameter DATA_WIDTH = 16;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
|
||||||
|
// Host side
|
||||||
|
reg req;
|
||||||
|
reg wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] addr;
|
||||||
|
reg [DATA_WIDTH-1:0] wdata;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] rdata;
|
||||||
|
wire ready;
|
||||||
|
|
||||||
|
// Memory side
|
||||||
|
wire mem_req;
|
||||||
|
wire mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||||
|
wire [DATA_WIDTH-1:0] mem_wdata;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] mem_rdata;
|
||||||
|
wire mem_ready;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Clock
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// DUT: Memory Interface
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
memory_interface #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH)
|
||||||
|
) dut (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
|
||||||
|
.req(req),
|
||||||
|
.wr(wr),
|
||||||
|
.addr(addr),
|
||||||
|
.wdata(wdata),
|
||||||
|
|
||||||
|
.rdata(rdata),
|
||||||
|
.ready(ready),
|
||||||
|
|
||||||
|
.mem_req(mem_req),
|
||||||
|
.mem_wr(mem_wr),
|
||||||
|
.mem_addr(mem_addr),
|
||||||
|
.mem_wdata(mem_wdata),
|
||||||
|
|
||||||
|
.mem_rdata(mem_rdata),
|
||||||
|
.mem_ready(mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Memory model
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
memory_model #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.DEPTH(4096),
|
||||||
|
.READ_LATENCY(2)
|
||||||
|
) memory (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
|
||||||
|
.req(mem_req),
|
||||||
|
.wr(mem_wr),
|
||||||
|
.addr(mem_addr),
|
||||||
|
.wdata(mem_wdata),
|
||||||
|
|
||||||
|
.rdata(mem_rdata),
|
||||||
|
.ready(mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test utilities
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
task write_mem;
|
||||||
|
input [ADDR_WIDTH-1:0] address;
|
||||||
|
input [DATA_WIDTH-1:0] data;
|
||||||
|
begin
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
addr <= address;
|
||||||
|
wdata <= data;
|
||||||
|
wr <= 1'b1;
|
||||||
|
req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
req <= 1'b0;
|
||||||
|
|
||||||
|
wait (ready);
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"WRITE addr=0x%08h data=0x%04h PASS",
|
||||||
|
address,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task read_mem;
|
||||||
|
input [ADDR_WIDTH-1:0] address;
|
||||||
|
input [DATA_WIDTH-1:0] expected;
|
||||||
|
|
||||||
|
reg [DATA_WIDTH-1:0] received;
|
||||||
|
|
||||||
|
begin
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
addr <= address;
|
||||||
|
wr <= 1'b0;
|
||||||
|
req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
req <= 1'b0;
|
||||||
|
|
||||||
|
wait (ready);
|
||||||
|
|
||||||
|
received = rdata;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
if (received === expected) begin
|
||||||
|
$display(
|
||||||
|
"READ addr=0x%08h data=0x%04h PASS",
|
||||||
|
address,
|
||||||
|
received
|
||||||
|
);
|
||||||
|
end else begin
|
||||||
|
$display(
|
||||||
|
"READ addr=0x%08h got=0x%04h expected=0x%04h FAIL",
|
||||||
|
address,
|
||||||
|
received,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test sequence
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
rst = 1'b1;
|
||||||
|
|
||||||
|
req = 1'b0;
|
||||||
|
wr = 1'b0;
|
||||||
|
addr = {ADDR_WIDTH{1'b0}};
|
||||||
|
wdata = {DATA_WIDTH{1'b0}};
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
repeat (3)
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
rst = 1'b0;
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("MEMORY INTERFACE V1 TEST");
|
||||||
|
$display("ADDR_WIDTH = %0d", ADDR_WIDTH);
|
||||||
|
$display("DATA_WIDTH = %0d", DATA_WIDTH);
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Test 1
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000001,
|
||||||
|
16'h1234
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000001,
|
||||||
|
16'h1234
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Test 2
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000010,
|
||||||
|
16'hABCD
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000010,
|
||||||
|
16'hABCD
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Test 3
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000100,
|
||||||
|
16'h55AA
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000100,
|
||||||
|
16'h55AA
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Test 4
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Consecutive different addresses
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000200,
|
||||||
|
16'h0001
|
||||||
|
);
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000201,
|
||||||
|
16'h0002
|
||||||
|
);
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000202,
|
||||||
|
16'h0003
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000200,
|
||||||
|
16'h0001
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000201,
|
||||||
|
16'h0002
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000202,
|
||||||
|
16'h0003
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Test 5
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Zero value
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000300,
|
||||||
|
16'h0000
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000300,
|
||||||
|
16'h0000
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Test 6
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Full 16-bit value
|
||||||
|
|
||||||
|
write_mem(
|
||||||
|
22'h000301,
|
||||||
|
16'hFFFF
|
||||||
|
);
|
||||||
|
|
||||||
|
read_mem(
|
||||||
|
22'h000301,
|
||||||
|
16'hFFFF
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Finished
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("MEMORY INTERFACE V1 TEST PASSED");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// VCD
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$dumpfile("sim/memory_interface.vcd");
|
||||||
|
$dumpvars(0, memory_interface_tb);
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
+73405
File diff suppressed because it is too large
Load Diff
Executable
+23299
File diff suppressed because one or more lines are too long
@@ -0,0 +1,625 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb;
|
||||||
|
|
||||||
|
localparam ADDR_WIDTH = 22;
|
||||||
|
localparam DATA_WIDTH = 16;
|
||||||
|
localparam CLK_PERIOD = 12.5; // 80 MHz
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// CLOCK / RESET
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #(CLK_PERIOD / 2.0) clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// NEURON MEMORY
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg start;
|
||||||
|
|
||||||
|
reg [ADDR_WIDTH-1:0] x_base;
|
||||||
|
reg [ADDR_WIDTH-1:0] w_base;
|
||||||
|
reg [ADDR_WIDTH-1:0] bias_addr;
|
||||||
|
|
||||||
|
wire signed [7:0] y;
|
||||||
|
wire busy;
|
||||||
|
wire done;
|
||||||
|
|
||||||
|
// neuron_memory -> memory_interface
|
||||||
|
wire neuron_mem_req;
|
||||||
|
wire neuron_mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] neuron_mem_addr;
|
||||||
|
wire signed [7:0] neuron_mem_wdata;
|
||||||
|
|
||||||
|
wire signed [7:0] neuron_mem_rdata;
|
||||||
|
wire neuron_mem_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// TB PRELOAD MASTER
|
||||||
|
//
|
||||||
|
// Direct 16-bit master.
|
||||||
|
// Used only before starting neuron_memory.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg tb_mem_req;
|
||||||
|
reg tb_mem_wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] tb_mem_addr;
|
||||||
|
reg [DATA_WIDTH-1:0] tb_mem_wdata;
|
||||||
|
reg tb_mem_lb_n;
|
||||||
|
reg tb_mem_ub_n;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] tb_mem_rdata;
|
||||||
|
wire tb_mem_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// SINGLE MASTER MUX
|
||||||
|
//
|
||||||
|
// 0 = TB preload master
|
||||||
|
// 1 = neuron_memory master
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg use_neuron_master;
|
||||||
|
|
||||||
|
wire master_req;
|
||||||
|
wire master_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] master_addr;
|
||||||
|
wire [DATA_WIDTH-1:0] master_wdata;
|
||||||
|
wire master_lb_n;
|
||||||
|
wire master_ub_n;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// MEMORY INTERFACE
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] memory_rdata;
|
||||||
|
wire memory_ready;
|
||||||
|
|
||||||
|
wire memory_mem_req;
|
||||||
|
wire memory_mem_wr;
|
||||||
|
wire [ADDR_WIDTH-1:0] memory_mem_addr;
|
||||||
|
wire [DATA_WIDTH-1:0] memory_mem_wdata;
|
||||||
|
wire memory_mem_lb_n;
|
||||||
|
wire memory_mem_ub_n;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] psram_mem_rdata;
|
||||||
|
wire psram_mem_ready;
|
||||||
|
|
||||||
|
assign master_req =
|
||||||
|
use_neuron_master ? neuron_mem_req : tb_mem_req;
|
||||||
|
|
||||||
|
assign master_wr =
|
||||||
|
use_neuron_master ? neuron_mem_wr : tb_mem_wr;
|
||||||
|
|
||||||
|
assign master_addr =
|
||||||
|
use_neuron_master ? (neuron_mem_addr >> 1) : tb_mem_addr;
|
||||||
|
|
||||||
|
assign master_wdata =
|
||||||
|
use_neuron_master
|
||||||
|
? (neuron_mem_addr[0]
|
||||||
|
? {neuron_mem_wdata, 8'h00}
|
||||||
|
: {8'h00, neuron_mem_wdata})
|
||||||
|
: tb_mem_wdata;
|
||||||
|
|
||||||
|
assign master_lb_n =
|
||||||
|
use_neuron_master
|
||||||
|
? (neuron_mem_addr[0] ? 1'b1 : 1'b0)
|
||||||
|
: tb_mem_lb_n;
|
||||||
|
|
||||||
|
assign master_ub_n =
|
||||||
|
use_neuron_master
|
||||||
|
? (neuron_mem_addr[0] ? 1'b0 : 1'b1)
|
||||||
|
: tb_mem_ub_n;
|
||||||
|
|
||||||
|
// Return path
|
||||||
|
assign tb_mem_rdata = memory_rdata;
|
||||||
|
assign tb_mem_ready = memory_ready;
|
||||||
|
|
||||||
|
assign neuron_mem_rdata =
|
||||||
|
neuron_mem_addr[0]
|
||||||
|
? memory_rdata[15:8]
|
||||||
|
: memory_rdata[7:0];
|
||||||
|
|
||||||
|
assign neuron_mem_ready = memory_ready;
|
||||||
|
|
||||||
|
memory_interface #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH)
|
||||||
|
) u_memory_if (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
|
||||||
|
.req(master_req),
|
||||||
|
.wr(master_wr),
|
||||||
|
.addr(master_addr),
|
||||||
|
.wdata(master_wdata),
|
||||||
|
.lb_n(master_lb_n),
|
||||||
|
.ub_n(master_ub_n),
|
||||||
|
|
||||||
|
.rdata(memory_rdata),
|
||||||
|
.ready(memory_ready),
|
||||||
|
|
||||||
|
.mem_req(memory_mem_req),
|
||||||
|
.mem_wr(memory_mem_wr),
|
||||||
|
.mem_addr(memory_mem_addr),
|
||||||
|
.mem_wdata(memory_mem_wdata),
|
||||||
|
.mem_lb_n(memory_mem_lb_n),
|
||||||
|
.mem_ub_n(memory_mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata(psram_mem_rdata),
|
||||||
|
.mem_ready(psram_mem_ready)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM PHYSICAL INTERFACE
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire [ADDR_WIDTH-1:0] psram_a;
|
||||||
|
wire [DATA_WIDTH-1:0] psram_dq;
|
||||||
|
|
||||||
|
wire psram_ce_n;
|
||||||
|
wire psram_oe_n;
|
||||||
|
wire psram_we_n;
|
||||||
|
wire psram_lb_n;
|
||||||
|
wire psram_ub_n;
|
||||||
|
wire psram_zz_n;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM CONTROLLER
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
psram_controller #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.CLK_FREQ_MHZ(80)
|
||||||
|
) u_psram_ctrl (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
|
||||||
|
.mem_req(memory_mem_req),
|
||||||
|
.mem_wr(memory_mem_wr),
|
||||||
|
.mem_addr(memory_mem_addr),
|
||||||
|
.mem_wdata(memory_mem_wdata),
|
||||||
|
.mem_lb_n(memory_mem_lb_n),
|
||||||
|
.mem_ub_n(memory_mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata(psram_mem_rdata),
|
||||||
|
.mem_ready(psram_mem_ready),
|
||||||
|
|
||||||
|
.psram_a(psram_a),
|
||||||
|
.psram_dq(psram_dq),
|
||||||
|
.psram_ce_n(psram_ce_n),
|
||||||
|
.psram_oe_n(psram_oe_n),
|
||||||
|
.psram_we_n(psram_we_n),
|
||||||
|
.psram_lb_n(psram_lb_n),
|
||||||
|
.psram_ub_n(psram_ub_n),
|
||||||
|
.psram_zz_n(psram_zz_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM MODEL
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
psram_model #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.DEPTH(16384)
|
||||||
|
) u_psram (
|
||||||
|
.clk(clk),
|
||||||
|
.a(psram_a),
|
||||||
|
.dq(psram_dq),
|
||||||
|
.ce_n(psram_ce_n),
|
||||||
|
.oe_n(psram_oe_n),
|
||||||
|
.we_n(psram_we_n),
|
||||||
|
.lb_n(psram_lb_n),
|
||||||
|
.ub_n(psram_ub_n),
|
||||||
|
.zz_n(psram_zz_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// NEURON MEMORY
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
neuron_memory #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(8),
|
||||||
|
.N_INPUTS(32),
|
||||||
|
.PARALLEL(8),
|
||||||
|
.ACC_WIDTH(32)
|
||||||
|
) u_neuron (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
.start(start),
|
||||||
|
|
||||||
|
.mem_req(neuron_mem_req),
|
||||||
|
.mem_wr(neuron_mem_wr),
|
||||||
|
.mem_addr(neuron_mem_addr),
|
||||||
|
.mem_wdata(neuron_mem_wdata),
|
||||||
|
|
||||||
|
.mem_rdata(neuron_mem_rdata),
|
||||||
|
.mem_ready(neuron_mem_ready),
|
||||||
|
|
||||||
|
.x_base(x_base),
|
||||||
|
.w_base(w_base),
|
||||||
|
.bias_addr(bias_addr),
|
||||||
|
|
||||||
|
.y(y),
|
||||||
|
.busy(busy),
|
||||||
|
.done(done)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// TB WORD WRITE
|
||||||
|
//
|
||||||
|
// Directly through:
|
||||||
|
//
|
||||||
|
// TB -> memory_interface -> psram_controller -> PSRAM
|
||||||
|
//
|
||||||
|
// No force.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task tb_write_word;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] addr_i;
|
||||||
|
input [15:0] data_i;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
tb_mem_addr <= addr_i;
|
||||||
|
tb_mem_wdata <= data_i;
|
||||||
|
tb_mem_wr <= 1'b1;
|
||||||
|
tb_mem_lb_n <= 1'b0;
|
||||||
|
tb_mem_ub_n <= 1'b0;
|
||||||
|
tb_mem_req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
tb_mem_req <= 1'b0;
|
||||||
|
|
||||||
|
wait (tb_mem_ready);
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PRELOAD 32 INT8 VALUES
|
||||||
|
//
|
||||||
|
// Two INT8 values per PSRAM word.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task preload_vector;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] base;
|
||||||
|
input signed [7:0] value;
|
||||||
|
|
||||||
|
integer k;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
for (k = 0; k < 32; k = k + 2) begin
|
||||||
|
|
||||||
|
tb_write_word(
|
||||||
|
base + (k >> 1),
|
||||||
|
{value, value}
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PRELOAD WEIGHTS
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task preload_weights;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] base;
|
||||||
|
input signed [7:0] value;
|
||||||
|
|
||||||
|
integer k;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
for (k = 0; k < 32; k = k + 2) begin
|
||||||
|
|
||||||
|
tb_write_word(
|
||||||
|
base + (k >> 1),
|
||||||
|
{value, value}
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PRELOAD BIAS
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task preload_bias;
|
||||||
|
|
||||||
|
input [ADDR_WIDTH-1:0] addr_i;
|
||||||
|
input signed [7:0] value;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
// Bias address is a BYTE address.
|
||||||
|
// Write a full word containing bias in low byte.
|
||||||
|
|
||||||
|
tb_write_word(
|
||||||
|
addr_i >> 1,
|
||||||
|
{8'h00, value}
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// RUN NEURON
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task run_neuron;
|
||||||
|
|
||||||
|
input signed [7:0] expected;
|
||||||
|
input [127:0] test_name;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
start <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
start <= 1'b0;
|
||||||
|
|
||||||
|
wait (done);
|
||||||
|
|
||||||
|
if (y !== expected) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("FAIL %s", test_name);
|
||||||
|
$display(
|
||||||
|
" got = %0d (0x%02x)",
|
||||||
|
y,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
$display(
|
||||||
|
" expected = %0d (0x%02x)",
|
||||||
|
expected,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"PASS %-16s y=%0d (0x%02x)",
|
||||||
|
test_name,
|
||||||
|
y,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// TEST
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Initial values
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
start = 1'b0;
|
||||||
|
|
||||||
|
x_base = 22'h000000;
|
||||||
|
w_base = 22'h000100;
|
||||||
|
bias_addr = 22'h000200;
|
||||||
|
|
||||||
|
tb_mem_req = 1'b0;
|
||||||
|
tb_mem_wr = 1'b0;
|
||||||
|
tb_mem_addr = 0;
|
||||||
|
tb_mem_wdata = 0;
|
||||||
|
tb_mem_lb_n = 1'b1;
|
||||||
|
tb_mem_ub_n = 1'b1;
|
||||||
|
|
||||||
|
use_neuron_master = 1'b0;
|
||||||
|
|
||||||
|
rst = 1'b1;
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// VCD
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
$dumpfile("sim/neuron_memory.vcd");
|
||||||
|
$dumpvars(0, tb);
|
||||||
|
|
||||||
|
repeat (5)
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
rst = 1'b0;
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Wait PSRAM initialization
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
wait (u_psram_ctrl.state == u_psram_ctrl.STATE_IDLE);
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("NEURON MEMORY END-TO-END TEST");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// PRELOAD PHASE
|
||||||
|
//
|
||||||
|
// TB is the ONLY memory master.
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("PRELOAD: X = 1");
|
||||||
|
preload_vector(
|
||||||
|
x_base,
|
||||||
|
8'sd1
|
||||||
|
);
|
||||||
|
|
||||||
|
$display("PRELOAD: W = 1");
|
||||||
|
preload_weights(
|
||||||
|
w_base,
|
||||||
|
8'sd1
|
||||||
|
);
|
||||||
|
|
||||||
|
$display("PRELOAD: BIAS = 0");
|
||||||
|
preload_bias(
|
||||||
|
bias_addr,
|
||||||
|
8'sd0
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// HAND OVER MEMORY BUS
|
||||||
|
//
|
||||||
|
// From this point neuron_memory is the only master.
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
use_neuron_master = 1'b1;
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("MEMORY MASTER -> neuron_memory");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// TEST 1
|
||||||
|
//
|
||||||
|
// 32 * 1 * 1 + 0 = 32
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
run_neuron(
|
||||||
|
8'sd32,
|
||||||
|
"SUM=32"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// TEST 2
|
||||||
|
//
|
||||||
|
// 32 * 1 * 4 = 128
|
||||||
|
// Saturated to 127.
|
||||||
|
//
|
||||||
|
// We must return control to TB to modify weights.
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
use_neuron_master = 1'b0;
|
||||||
|
|
||||||
|
preload_weights(
|
||||||
|
w_base,
|
||||||
|
8'sd4
|
||||||
|
);
|
||||||
|
|
||||||
|
preload_bias(
|
||||||
|
bias_addr,
|
||||||
|
8'sd0
|
||||||
|
);
|
||||||
|
|
||||||
|
use_neuron_master = 1'b1;
|
||||||
|
|
||||||
|
run_neuron(
|
||||||
|
8'sd127,
|
||||||
|
"SATURATION"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// TEST 3
|
||||||
|
//
|
||||||
|
// 32 * 1 * (-1) = -32
|
||||||
|
// ReLU -> 0
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
use_neuron_master = 1'b0;
|
||||||
|
|
||||||
|
preload_weights(
|
||||||
|
w_base,
|
||||||
|
-8'sd1
|
||||||
|
);
|
||||||
|
|
||||||
|
preload_bias(
|
||||||
|
bias_addr,
|
||||||
|
8'sd0
|
||||||
|
);
|
||||||
|
|
||||||
|
use_neuron_master = 1'b1;
|
||||||
|
|
||||||
|
run_neuron(
|
||||||
|
8'sd0,
|
||||||
|
"RELU"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// TEST 4
|
||||||
|
//
|
||||||
|
// 32 * 1 * 1 + 10 = 42
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
use_neuron_master = 1'b0;
|
||||||
|
|
||||||
|
preload_weights(
|
||||||
|
w_base,
|
||||||
|
8'sd1
|
||||||
|
);
|
||||||
|
|
||||||
|
preload_bias(
|
||||||
|
bias_addr,
|
||||||
|
8'sd10
|
||||||
|
);
|
||||||
|
|
||||||
|
use_neuron_master = 1'b1;
|
||||||
|
|
||||||
|
run_neuron(
|
||||||
|
8'sd42,
|
||||||
|
"BIAS=10"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// FINAL
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("NEURON MEMORY TEST PASSED");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM -> INT8 -> NEURON : PASS");
|
||||||
|
$display("SUM : PASS");
|
||||||
|
$display("BIAS : PASS");
|
||||||
|
$display("ReLU : PASS");
|
||||||
|
$display("SATURATION : PASS");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
+3292
-1609
File diff suppressed because one or more lines are too long
Executable
+5430
File diff suppressed because it is too large
Load Diff
Executable
+4242
File diff suppressed because it is too large
Load Diff
Executable
+2238
File diff suppressed because it is too large
Load Diff
+1801
-1633
File diff suppressed because it is too large
Load Diff
Executable
+5404
File diff suppressed because it is too large
Load Diff
Executable
+3350
File diff suppressed because it is too large
Load Diff
Executable
+5478
File diff suppressed because it is too large
Load Diff
+98
-192
@@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
module tb;
|
module tb;
|
||||||
|
|
||||||
parameter DATA_WIDTH = 16;
|
parameter DATA_WIDTH = 8;
|
||||||
parameter FRAC_BITS = 8;
|
parameter N_INPUTS = 256;
|
||||||
parameter N_INPUTS = 32;
|
|
||||||
parameter N_NEURONS = 4;
|
parameter N_NEURONS = 4;
|
||||||
parameter PARALLEL = 8;
|
parameter PARALLEL = 32;
|
||||||
parameter ACC_WIDTH = 40;
|
parameter ACC_WIDTH = 32;
|
||||||
|
|
||||||
reg clk;
|
reg clk;
|
||||||
reg rst;
|
reg rst;
|
||||||
@@ -21,20 +20,17 @@ module tb;
|
|||||||
reg signed [DATA_WIDTH*N_NEURONS-1:0]
|
reg signed [DATA_WIDTH*N_NEURONS-1:0]
|
||||||
bias_bus;
|
bias_bus;
|
||||||
|
|
||||||
wire signed [DATA_WIDTH*N_NEURONS-1:0]
|
wire signed [DATA_WIDTH*N_NEURONS-1:0] y_bus;
|
||||||
y_bus;
|
|
||||||
|
|
||||||
wire busy;
|
wire busy;
|
||||||
wire done;
|
wire done;
|
||||||
|
|
||||||
integer i;
|
integer i;
|
||||||
integer n;
|
|
||||||
integer errors;
|
integer errors;
|
||||||
integer expected;
|
integer expected;
|
||||||
|
|
||||||
layer #(
|
layer #(
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
.FRAC_BITS(FRAC_BITS),
|
|
||||||
.N_INPUTS(N_INPUTS),
|
.N_INPUTS(N_INPUTS),
|
||||||
.N_NEURONS(N_NEURONS),
|
.N_NEURONS(N_NEURONS),
|
||||||
.PARALLEL(PARALLEL),
|
.PARALLEL(PARALLEL),
|
||||||
@@ -56,13 +52,6 @@ module tb;
|
|||||||
forever #5 clk = ~clk;
|
forever #5 clk = ~clk;
|
||||||
end
|
end
|
||||||
|
|
||||||
function signed [15:0] q8_8;
|
|
||||||
input real value;
|
|
||||||
begin
|
|
||||||
q8_8 = $rtoi(value * 256.0);
|
|
||||||
end
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
task run_layer;
|
task run_layer;
|
||||||
begin
|
begin
|
||||||
@(posedge clk);
|
@(posedge clk);
|
||||||
@@ -94,107 +83,81 @@ module tb;
|
|||||||
rst = 0;
|
rst = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All inputs = 1.0
|
* Tutti gli input = 1
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
x_bus[i*DATA_WIDTH +: DATA_WIDTH] = q8_8(1.0);
|
x_bus[i*DATA_WIDTH +: DATA_WIDTH] = 8'sd1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 0: weight = 1.0
|
* N0:
|
||||||
* result = N_INPUTS
|
* primi 32 pesi = 1
|
||||||
*/
|
* restanti 224 = 0
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
*
|
||||||
weights_bus[
|
* Risultato = 32
|
||||||
0*N_INPUTS*DATA_WIDTH +
|
*
|
||||||
i*DATA_WIDTH +:
|
* Questo verifica che l'accumulatore
|
||||||
DATA_WIDTH
|
* attraversi correttamente tutti gli 8 gruppi.
|
||||||
] = q8_8(1.0);
|
*/
|
||||||
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
|
weights_bus[
|
||||||
|
0*N_INPUTS*DATA_WIDTH +
|
||||||
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
|
] = (i < 32) ? 8'sd1 : 8'sd0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 1: weight = 0.5
|
* N1:
|
||||||
* result = N_INPUTS / 2
|
* tutti i 256 pesi = 0
|
||||||
*/
|
* bias = 10
|
||||||
if (N_NEURONS > 1)
|
*
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
* Risultato = 10
|
||||||
weights_bus[
|
*/
|
||||||
1*N_INPUTS*DATA_WIDTH +
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
i*DATA_WIDTH +:
|
weights_bus[
|
||||||
DATA_WIDTH
|
1*N_INPUTS*DATA_WIDTH +
|
||||||
] = q8_8(0.5);
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
|
] = 8'sd0;
|
||||||
|
|
||||||
/*
|
bias_bus[1*DATA_WIDTH +: DATA_WIDTH] = 8'sd10;
|
||||||
* Neuron 2: weight = -0.5
|
|
||||||
* ReLU -> 0
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 2)
|
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
|
||||||
weights_bus[
|
|
||||||
2*N_INPUTS*DATA_WIDTH +
|
|
||||||
i*DATA_WIDTH +:
|
|
||||||
DATA_WIDTH
|
|
||||||
] = q8_8(-0.5);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 3: weight = 2.0
|
* N2:
|
||||||
* Large positive result -> saturation
|
* tutti i pesi = -1
|
||||||
*/
|
*
|
||||||
if (N_NEURONS > 3)
|
* Risultato = -256
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
* ReLU -> 0
|
||||||
weights_bus[
|
*/
|
||||||
3*N_INPUTS*DATA_WIDTH +
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
i*DATA_WIDTH +:
|
weights_bus[
|
||||||
DATA_WIDTH
|
2*N_INPUTS*DATA_WIDTH +
|
||||||
] = q8_8(2.0);
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
|
] = -8'sd1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 4: zero weights + bias +1
|
* N3:
|
||||||
*/
|
* primi 32 pesi = 4
|
||||||
if (N_NEURONS > 4)
|
* restanti = 0
|
||||||
bias_bus[4*DATA_WIDTH +: DATA_WIDTH] = q8_8(1.0);
|
*
|
||||||
|
* Risultato = 128
|
||||||
/*
|
* Saturazione INT8 -> 127
|
||||||
* Neuron 5: zero weights + bias -1
|
*/
|
||||||
* ReLU -> 0
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
*/
|
weights_bus[
|
||||||
if (N_NEURONS > 5)
|
3*N_INPUTS*DATA_WIDTH +
|
||||||
bias_bus[5*DATA_WIDTH +: DATA_WIDTH] = q8_8(-1.0);
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
/*
|
] = (i < 32) ? 8'sd4 : 8'sd0;
|
||||||
* Neuron 6: weight 1.0 + bias -1.0
|
|
||||||
* result = N_INPUTS - 1
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 6) begin
|
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
|
||||||
weights_bus[
|
|
||||||
6*N_INPUTS*DATA_WIDTH +
|
|
||||||
i*DATA_WIDTH +:
|
|
||||||
DATA_WIDTH
|
|
||||||
] = q8_8(1.0);
|
|
||||||
|
|
||||||
bias_bus[6*DATA_WIDTH +: DATA_WIDTH] = q8_8(-1.0);
|
|
||||||
end
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Neuron 7: weight 0.25 + bias 1
|
|
||||||
* result = N_INPUTS/4 + 1
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 7) begin
|
|
||||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
|
||||||
weights_bus[
|
|
||||||
7*N_INPUTS*DATA_WIDTH +
|
|
||||||
i*DATA_WIDTH +:
|
|
||||||
DATA_WIDTH
|
|
||||||
] = q8_8(0.25);
|
|
||||||
|
|
||||||
bias_bus[7*DATA_WIDTH +: DATA_WIDTH] = q8_8(1.0);
|
|
||||||
end
|
|
||||||
|
|
||||||
$display("");
|
$display("");
|
||||||
$display("========================================");
|
$display("========================================");
|
||||||
$display("PARAMETRIC LAYER TEST");
|
$display("INT8 / INT32 PARAMETRIC LAYER TEST");
|
||||||
$display("N_INPUTS = %0d", N_INPUTS);
|
$display("N_INPUTS = %0d", N_INPUTS);
|
||||||
$display("N_NEURONS = %0d", N_NEURONS);
|
$display("N_NEURONS = %0d", N_NEURONS);
|
||||||
$display("PARALLEL = %0d", PARALLEL);
|
$display("PARALLEL = %0d", PARALLEL);
|
||||||
|
$display("DATA_WIDTH = %0d", DATA_WIDTH);
|
||||||
|
$display("ACC_WIDTH = %0d", ACC_WIDTH);
|
||||||
$display("========================================");
|
$display("========================================");
|
||||||
|
|
||||||
run_layer;
|
run_layer;
|
||||||
@@ -202,40 +165,45 @@ module tb;
|
|||||||
/*
|
/*
|
||||||
* Neuron 0
|
* Neuron 0
|
||||||
*/
|
*/
|
||||||
expected = N_INPUTS * 256;
|
expected = 32;
|
||||||
|
|
||||||
if (y_bus[0*16 +: 16] !== expected[15:0]) begin
|
if ($signed(y_bus[0*DATA_WIDTH +: DATA_WIDTH]) !== expected) begin
|
||||||
$display("FAIL N0: got %0d expected %0d",
|
$display("FAIL N0: got %0d expected %0d",
|
||||||
y_bus[0*16 +: 16], expected);
|
$signed(y_bus[0*DATA_WIDTH +: DATA_WIDTH]),
|
||||||
|
expected);
|
||||||
errors = errors + 1;
|
errors = errors + 1;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
$display("PASS N0: %0d", y_bus[0*16 +: 16]);
|
$display("PASS N0: %0d", $signed(y_bus[0*DATA_WIDTH +: DATA_WIDTH]));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 1
|
* Neuron 1
|
||||||
*/
|
*/
|
||||||
if (N_NEURONS > 1) begin
|
if (N_NEURONS > 1) begin
|
||||||
expected = (N_INPUTS * 128);
|
|
||||||
|
|
||||||
if (y_bus[1*16 +: 16] !== expected[15:0]) begin
|
expected = 10;
|
||||||
|
|
||||||
|
if ($signed(y_bus[1*DATA_WIDTH +: DATA_WIDTH]) !== expected) begin
|
||||||
$display("FAIL N1: got %0d expected %0d",
|
$display("FAIL N1: got %0d expected %0d",
|
||||||
y_bus[1*16 +: 16], expected);
|
$signed(y_bus[1*DATA_WIDTH +: DATA_WIDTH]),
|
||||||
|
expected);
|
||||||
errors = errors + 1;
|
errors = errors + 1;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
$display("PASS N1: %0d", y_bus[1*16 +: 16]);
|
$display("PASS N1: %0d",
|
||||||
|
$signed(y_bus[1*DATA_WIDTH +: DATA_WIDTH]));
|
||||||
end
|
end
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 2
|
* Neuron 2
|
||||||
*/
|
*/
|
||||||
if (N_NEURONS > 2) begin
|
if (N_NEURONS > 2) begin
|
||||||
|
|
||||||
expected = 0;
|
expected = 0;
|
||||||
|
|
||||||
if (y_bus[2*16 +: 16] !== 16'sd0) begin
|
if ($signed(y_bus[2*DATA_WIDTH +: DATA_WIDTH]) !== 0) begin
|
||||||
$display("FAIL N2: got %0d expected 0",
|
$display("FAIL N2: got %0d expected 0",
|
||||||
y_bus[2*16 +: 16]);
|
$signed(y_bus[2*DATA_WIDTH +: DATA_WIDTH]));
|
||||||
errors = errors + 1;
|
errors = errors + 1;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -243,86 +211,24 @@ module tb;
|
|||||||
end
|
end
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Neuron 3:
|
* Neuron 3
|
||||||
* weight = 2.0
|
*
|
||||||
* result = N_INPUTS * 2.0
|
* 32 * 4 = 128
|
||||||
*
|
* INT8 positive saturation -> 127
|
||||||
* Saturation only occurs when result > 127.996...
|
*/
|
||||||
*/
|
|
||||||
if (N_NEURONS > 3) begin
|
if (N_NEURONS > 3) begin
|
||||||
|
|
||||||
expected = N_INPUTS * 2 * 256;
|
expected = 127;
|
||||||
|
|
||||||
if (expected > 32767)
|
if ($signed(y_bus[3*DATA_WIDTH +: DATA_WIDTH]) !== expected) begin
|
||||||
expected = 32767;
|
$display("FAIL N3: got %0d expected %0d",
|
||||||
|
$signed(y_bus[3*DATA_WIDTH +: DATA_WIDTH]),
|
||||||
if (y_bus[3*16 +: 16] !== expected[15:0]) begin
|
expected);
|
||||||
$display("FAIL N3: got %0d expected %0d",
|
|
||||||
y_bus[3*16 +: 16], expected);
|
|
||||||
errors = errors + 1;
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
if (expected == 32767)
|
|
||||||
$display("PASS N3: %0d (saturation)", y_bus[3*16 +: 16]);
|
|
||||||
else
|
|
||||||
$display("PASS N3: %0d", y_bus[3*16 +: 16]);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Neuron 4
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 4) begin
|
|
||||||
if (y_bus[4*16 +: 16] !== 16'sd256) begin
|
|
||||||
$display("FAIL N4: got %0d expected 256",
|
|
||||||
y_bus[4*16 +: 16]);
|
|
||||||
errors = errors + 1;
|
errors = errors + 1;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
$display("PASS N4: 256 (bias)");
|
$display("PASS N3: %0d (saturation)",
|
||||||
end
|
$signed(y_bus[3*DATA_WIDTH +: DATA_WIDTH]));
|
||||||
|
|
||||||
/*
|
|
||||||
* Neuron 5
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 5) begin
|
|
||||||
if (y_bus[5*16 +: 16] !== 16'sd0) begin
|
|
||||||
$display("FAIL N5: got %0d expected 0",
|
|
||||||
y_bus[5*16 +: 16]);
|
|
||||||
errors = errors + 1;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
$display("PASS N5: 0 (negative bias + ReLU)");
|
|
||||||
end
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Neuron 6
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 6) begin
|
|
||||||
expected = (N_INPUTS - 1) * 256;
|
|
||||||
|
|
||||||
if (y_bus[6*16 +: 16] !== expected[15:0]) begin
|
|
||||||
$display("FAIL N6: got %0d expected %0d",
|
|
||||||
y_bus[6*16 +: 16], expected);
|
|
||||||
errors = errors + 1;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
$display("PASS N6: %0d", y_bus[6*16 +: 16]);
|
|
||||||
end
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Neuron 7
|
|
||||||
*/
|
|
||||||
if (N_NEURONS > 7) begin
|
|
||||||
expected = (N_INPUTS / 4 + 1) * 256;
|
|
||||||
|
|
||||||
if (y_bus[7*16 +: 16] !== expected[15:0]) begin
|
|
||||||
$display("FAIL N7: got %0d expected %0d",
|
|
||||||
y_bus[7*16 +: 16], expected);
|
|
||||||
errors = errors + 1;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
$display("PASS N7: %0d", y_bus[7*16 +: 16]);
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if (busy !== 0) begin
|
if (busy !== 0) begin
|
||||||
@@ -339,9 +245,9 @@ module tb;
|
|||||||
$display("========================================");
|
$display("========================================");
|
||||||
|
|
||||||
if (errors == 0)
|
if (errors == 0)
|
||||||
$display("PARAMETRIC TEST PASSED");
|
$display("INT8 PARAMETRIC TEST PASSED");
|
||||||
else
|
else
|
||||||
$display("PARAMETRIC TEST FAILED: %0d errors", errors);
|
$display("INT8 PARAMETRIC TEST FAILED: %0d errors", errors);
|
||||||
|
|
||||||
$display("========================================");
|
$display("========================================");
|
||||||
$display("");
|
$display("");
|
||||||
|
|||||||
+427102
File diff suppressed because it is too large
Load Diff
Executable
+21957
File diff suppressed because one or more lines are too long
@@ -0,0 +1,498 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb;
|
||||||
|
|
||||||
|
localparam ADDR_WIDTH = 22;
|
||||||
|
localparam DATA_WIDTH = 16;
|
||||||
|
localparam CLK_PERIOD = 12.5; // 80 MHz
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Memory Interface
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg mem_req;
|
||||||
|
reg mem_wr;
|
||||||
|
reg [ADDR_WIDTH-1:0] mem_addr;
|
||||||
|
reg [DATA_WIDTH-1:0] mem_wdata;
|
||||||
|
reg mem_lb_n;
|
||||||
|
reg mem_ub_n;
|
||||||
|
|
||||||
|
wire [DATA_WIDTH-1:0] mem_rdata;
|
||||||
|
wire mem_ready;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
wire [ADDR_WIDTH-1:0] psram_a;
|
||||||
|
wire [DATA_WIDTH-1:0] psram_dq;
|
||||||
|
|
||||||
|
wire psram_ce_n;
|
||||||
|
wire psram_oe_n;
|
||||||
|
wire psram_we_n;
|
||||||
|
wire psram_lb_n;
|
||||||
|
wire psram_ub_n;
|
||||||
|
wire psram_zz_n;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Stress-test variables
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
integer stress_i;
|
||||||
|
integer stress_addr;
|
||||||
|
reg [15:0] stress_data;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// DUT
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
psram_controller #(
|
||||||
|
.ADDR_WIDTH (ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH (DATA_WIDTH),
|
||||||
|
.CLK_FREQ_MHZ (80)
|
||||||
|
) dut (
|
||||||
|
.clk (clk),
|
||||||
|
.rst (rst),
|
||||||
|
|
||||||
|
.mem_req (mem_req),
|
||||||
|
.mem_wr (mem_wr),
|
||||||
|
.mem_addr (mem_addr),
|
||||||
|
.mem_wdata (mem_wdata),
|
||||||
|
.mem_lb_n (mem_lb_n),
|
||||||
|
.mem_ub_n (mem_ub_n),
|
||||||
|
|
||||||
|
.mem_rdata (mem_rdata),
|
||||||
|
.mem_ready (mem_ready),
|
||||||
|
|
||||||
|
.psram_a (psram_a),
|
||||||
|
.psram_dq (psram_dq),
|
||||||
|
|
||||||
|
.psram_ce_n(psram_ce_n),
|
||||||
|
.psram_oe_n(psram_oe_n),
|
||||||
|
.psram_we_n(psram_we_n),
|
||||||
|
.psram_lb_n(psram_lb_n),
|
||||||
|
.psram_ub_n(psram_ub_n),
|
||||||
|
.psram_zz_n(psram_zz_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// PSRAM model
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
psram_model #(
|
||||||
|
.ADDR_WIDTH(ADDR_WIDTH),
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.DEPTH(16384)
|
||||||
|
) memory (
|
||||||
|
.clk (clk),
|
||||||
|
.a (psram_a),
|
||||||
|
.dq (psram_dq),
|
||||||
|
.ce_n (psram_ce_n),
|
||||||
|
.oe_n (psram_oe_n),
|
||||||
|
.we_n (psram_we_n),
|
||||||
|
.lb_n (psram_lb_n),
|
||||||
|
.ub_n (psram_ub_n),
|
||||||
|
.zz_n (psram_zz_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Clock
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
|
||||||
|
forever #(CLK_PERIOD / 2.0)
|
||||||
|
clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// VCD
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$dumpfile("sim/psram_controller.vcd");
|
||||||
|
$dumpvars(0, tb);
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Helper: write word with byte enables
|
||||||
|
//
|
||||||
|
// lb_n = 0 -> low byte enabled
|
||||||
|
// ub_n = 0 -> high byte enabled
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task write_word;
|
||||||
|
input [ADDR_WIDTH-1:0] addr;
|
||||||
|
input [DATA_WIDTH-1:0] data;
|
||||||
|
input lb;
|
||||||
|
input ub;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
mem_addr <= addr;
|
||||||
|
mem_wdata <= data;
|
||||||
|
mem_wr <= 1'b1;
|
||||||
|
mem_lb_n <= lb;
|
||||||
|
mem_ub_n <= ub;
|
||||||
|
mem_req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
|
||||||
|
wait (mem_ready);
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"WRITE addr=0x%08x data=0x%04x LB#=%b UB#=%b PASS",
|
||||||
|
addr,
|
||||||
|
data,
|
||||||
|
lb,
|
||||||
|
ub
|
||||||
|
);
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Helper: read full word
|
||||||
|
//
|
||||||
|
// For normal word reads both bytes are enabled.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
task read_word;
|
||||||
|
input [ADDR_WIDTH-1:0] addr;
|
||||||
|
input [DATA_WIDTH-1:0] expected;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
mem_addr <= addr;
|
||||||
|
mem_wr <= 1'b0;
|
||||||
|
mem_lb_n <= 1'b0;
|
||||||
|
mem_ub_n <= 1'b0;
|
||||||
|
mem_req <= 1'b1;
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
mem_req <= 1'b0;
|
||||||
|
|
||||||
|
wait (mem_ready);
|
||||||
|
|
||||||
|
if (mem_rdata !== expected) begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"READ addr=0x%08x FAIL got=0x%04x expected=0x%04x",
|
||||||
|
addr,
|
||||||
|
mem_rdata,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
$display(
|
||||||
|
"READ addr=0x%08x data=0x%04x PASS",
|
||||||
|
addr,
|
||||||
|
mem_rdata
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Test
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
mem_req = 1'b0;
|
||||||
|
mem_wr = 1'b0;
|
||||||
|
mem_addr = 0;
|
||||||
|
mem_wdata = 0;
|
||||||
|
|
||||||
|
// Both bytes disabled while idle
|
||||||
|
mem_lb_n = 1'b1;
|
||||||
|
mem_ub_n = 1'b1;
|
||||||
|
|
||||||
|
rst = 1'b1;
|
||||||
|
|
||||||
|
repeat (5)
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
rst = 1'b0;
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM CONTROLLER V1 TEST");
|
||||||
|
$display("80 MHz");
|
||||||
|
$display("4M x 16 PSRAM");
|
||||||
|
$display("70 ns asynchronous timing");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
wait (dut.state == dut.STATE_IDLE);
|
||||||
|
|
||||||
|
$display("PSRAM initialization complete");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Basic writes / reads
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_word(22'h000001, 16'h1234, 1'b0, 1'b0);
|
||||||
|
read_word (22'h000001, 16'h1234);
|
||||||
|
|
||||||
|
write_word(22'h000010, 16'hABCD, 1'b0, 1'b0);
|
||||||
|
read_word (22'h000010, 16'hABCD);
|
||||||
|
|
||||||
|
write_word(22'h000100, 16'h55AA, 1'b0, 1'b0);
|
||||||
|
read_word (22'h000100, 16'h55AA);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Consecutive words
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_word(22'h000200, 16'h0001, 1'b0, 1'b0);
|
||||||
|
write_word(22'h000201, 16'h0002, 1'b0, 1'b0);
|
||||||
|
write_word(22'h000202, 16'h0003, 1'b0, 1'b0);
|
||||||
|
|
||||||
|
read_word(22'h000200, 16'h0001);
|
||||||
|
read_word(22'h000201, 16'h0002);
|
||||||
|
read_word(22'h000202, 16'h0003);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Edge values
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_word(22'h000300, 16'h0000, 1'b0, 1'b0);
|
||||||
|
read_word (22'h000300, 16'h0000);
|
||||||
|
|
||||||
|
write_word(22'h000301, 16'hFFFF, 1'b0, 1'b0);
|
||||||
|
read_word (22'h000301, 16'hFFFF);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// High address
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
write_word(22'h003FFF, 16'hCAFE, 1'b0, 1'b0);
|
||||||
|
read_word (22'h003FFF, 16'hCAFE);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Basic test passed
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM CONTROLLER BASIC TEST PASSED");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// BYTE ENABLE TEST
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM BYTE ENABLE TEST");
|
||||||
|
$display("LB# / UB#");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Start from known value
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
write_word(
|
||||||
|
22'h000400,
|
||||||
|
16'h1234,
|
||||||
|
1'b0,
|
||||||
|
1'b0
|
||||||
|
);
|
||||||
|
|
||||||
|
read_word(
|
||||||
|
22'h000400,
|
||||||
|
16'h1234
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// LOW BYTE ONLY
|
||||||
|
//
|
||||||
|
// Initial: 0x1234
|
||||||
|
// Write: 0x00AA
|
||||||
|
//
|
||||||
|
// LB# = 0 -> low byte written
|
||||||
|
// UB# = 1 -> high byte preserved
|
||||||
|
//
|
||||||
|
// Expected: 0x12AA
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("LOW BYTE ONLY");
|
||||||
|
$display("Initial = 0x1234");
|
||||||
|
$display("Write = 0x00AA");
|
||||||
|
$display("LB#=0 UB#=1");
|
||||||
|
$display("Expected= 0x12AA");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
write_word(
|
||||||
|
22'h000400,
|
||||||
|
16'h00AA,
|
||||||
|
1'b0,
|
||||||
|
1'b1
|
||||||
|
);
|
||||||
|
|
||||||
|
read_word(
|
||||||
|
22'h000400,
|
||||||
|
16'h12AA
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// HIGH BYTE ONLY
|
||||||
|
//
|
||||||
|
// Current: 0x12AA
|
||||||
|
// Write: 0xBB00
|
||||||
|
//
|
||||||
|
// LB# = 1 -> low byte preserved
|
||||||
|
// UB# = 0 -> high byte written
|
||||||
|
//
|
||||||
|
// Expected: 0xBBAA
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("HIGH BYTE ONLY");
|
||||||
|
$display("Initial = 0x12AA");
|
||||||
|
$display("Write = 0xBB00");
|
||||||
|
$display("LB#=1 UB#=0");
|
||||||
|
$display("Expected= 0xBBAA");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
write_word(
|
||||||
|
22'h000400,
|
||||||
|
16'hBB00,
|
||||||
|
1'b1,
|
||||||
|
1'b0
|
||||||
|
);
|
||||||
|
|
||||||
|
read_word(
|
||||||
|
22'h000400,
|
||||||
|
16'hBBAA
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// FULL WORD
|
||||||
|
//
|
||||||
|
// Current: 0xBBAA
|
||||||
|
// Write: 0xCCDD
|
||||||
|
//
|
||||||
|
// LB# = 0 -> low byte written
|
||||||
|
// UB# = 0 -> high byte written
|
||||||
|
//
|
||||||
|
// Expected: 0xCCDD
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("FULL WORD");
|
||||||
|
$display("Initial = 0xBBAA");
|
||||||
|
$display("Write = 0xCCDD");
|
||||||
|
$display("LB#=0 UB#=0");
|
||||||
|
$display("Expected= 0xCCDD");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
write_word(
|
||||||
|
22'h000400,
|
||||||
|
16'hCCDD,
|
||||||
|
1'b0,
|
||||||
|
1'b0
|
||||||
|
);
|
||||||
|
|
||||||
|
read_word(
|
||||||
|
22'h000400,
|
||||||
|
16'hCCDD
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// BYTE ENABLE TEST PASSED
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM BYTE ENABLE TEST PASSED");
|
||||||
|
$display("LOW BYTE : PASS");
|
||||||
|
$display("HIGH BYTE : PASS");
|
||||||
|
$display("FULL WORD : PASS");
|
||||||
|
$display("PRESERVE : PASS");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// STRESS TEST
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM STRESS TEST");
|
||||||
|
$display("2048 WRITE + READ transactions");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
for (stress_i = 0;
|
||||||
|
stress_i < 2048;
|
||||||
|
stress_i = stress_i + 1) begin
|
||||||
|
|
||||||
|
stress_addr =
|
||||||
|
((stress_i * 7919) ^ (stress_i << 5)) & 16'h3FFF;
|
||||||
|
|
||||||
|
stress_data =
|
||||||
|
((stress_i * 1237) ^ 16'hA5A5);
|
||||||
|
|
||||||
|
write_word(
|
||||||
|
stress_addr,
|
||||||
|
stress_data,
|
||||||
|
1'b0,
|
||||||
|
1'b0
|
||||||
|
);
|
||||||
|
|
||||||
|
read_word(
|
||||||
|
stress_addr,
|
||||||
|
stress_data
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((stress_i % 128) == 0)
|
||||||
|
$display(
|
||||||
|
"STRESS %0d / 2048 PASS",
|
||||||
|
stress_i
|
||||||
|
);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Final result
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM CONTROLLER V1 TEST PASSED");
|
||||||
|
$display("2048 WRITE + READ stress transactions");
|
||||||
|
$display("BYTE ENABLE TEST PASSED");
|
||||||
|
$display("========================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,502 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module psram_model #(
|
||||||
|
parameter ADDR_WIDTH = 22,
|
||||||
|
parameter DATA_WIDTH = 16,
|
||||||
|
parameter DEPTH = 16384
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire [ADDR_WIDTH-1:0] a,
|
||||||
|
inout wire [DATA_WIDTH-1:0] dq,
|
||||||
|
input wire ce_n,
|
||||||
|
input wire oe_n,
|
||||||
|
input wire we_n,
|
||||||
|
input wire lb_n,
|
||||||
|
input wire ub_n,
|
||||||
|
input wire zz_n
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// IS66WVE4M16EBLL-70BLI - Rev. D3
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
localparam realtime TAA_NS = 70.0;
|
||||||
|
localparam realtime TRC_NS = 70.0;
|
||||||
|
localparam realtime TOE_NS = 20.0;
|
||||||
|
localparam realtime TOH_NS = 5.0;
|
||||||
|
|
||||||
|
localparam realtime TLZ_NS = 10.0;
|
||||||
|
localparam realtime THZ_NS = 8.0;
|
||||||
|
|
||||||
|
localparam realtime TWC_NS = 70.0;
|
||||||
|
localparam realtime TAW_NS = 70.0;
|
||||||
|
localparam realtime TCW_NS = 70.0;
|
||||||
|
localparam realtime TWP_NS = 46.0;
|
||||||
|
localparam realtime TDW_NS = 23.0;
|
||||||
|
localparam realtime TDH_NS = 0.0;
|
||||||
|
localparam realtime TWR_NS = 0.0;
|
||||||
|
localparam realtime TWPH_NS = 10.0;
|
||||||
|
|
||||||
|
localparam realtime TPU_NS = 150000.0;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Memory
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
|
||||||
|
|
||||||
|
reg [DATA_WIDTH-1:0] dq_out;
|
||||||
|
reg dq_oe;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
assign dq = dq_oe ? dq_out : {DATA_WIDTH{1'bz}};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Timing state
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
realtime powerup_time;
|
||||||
|
|
||||||
|
realtime ce_low_time;
|
||||||
|
realtime ce_high_time;
|
||||||
|
|
||||||
|
realtime oe_low_time;
|
||||||
|
realtime oe_high_time;
|
||||||
|
|
||||||
|
realtime we_low_time;
|
||||||
|
realtime we_high_time;
|
||||||
|
|
||||||
|
realtime last_read_start;
|
||||||
|
realtime last_write_start;
|
||||||
|
|
||||||
|
realtime addr_valid_time;
|
||||||
|
realtime data_valid_time;
|
||||||
|
|
||||||
|
reg read_active;
|
||||||
|
reg write_active;
|
||||||
|
|
||||||
|
reg [ADDR_WIDTH-1:0] active_addr;
|
||||||
|
reg [DATA_WIDTH-1:0] active_wdata;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Initialization
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
for (i = 0; i < DEPTH; i = i + 1)
|
||||||
|
mem[i] = 16'h0000;
|
||||||
|
|
||||||
|
dq_out = 16'h0000;
|
||||||
|
dq_oe = 1'b0;
|
||||||
|
|
||||||
|
powerup_time = $realtime;
|
||||||
|
|
||||||
|
ce_low_time = 0.0;
|
||||||
|
ce_high_time = 0.0;
|
||||||
|
|
||||||
|
oe_low_time = 0.0;
|
||||||
|
oe_high_time = 0.0;
|
||||||
|
|
||||||
|
we_low_time = 0.0;
|
||||||
|
we_high_time = 0.0;
|
||||||
|
|
||||||
|
last_read_start = -1.0;
|
||||||
|
last_write_start = -1.0;
|
||||||
|
|
||||||
|
addr_valid_time = 0.0;
|
||||||
|
data_valid_time = 0.0;
|
||||||
|
|
||||||
|
read_active = 1'b0;
|
||||||
|
write_active = 1'b0;
|
||||||
|
|
||||||
|
active_addr = 0;
|
||||||
|
active_wdata = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// FUNCTIONAL READ MODEL
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
|
||||||
|
dq_oe = 1'b0;
|
||||||
|
dq_out = 16'h0000;
|
||||||
|
|
||||||
|
if (zz_n &&
|
||||||
|
!ce_n &&
|
||||||
|
!oe_n &&
|
||||||
|
we_n) begin
|
||||||
|
|
||||||
|
if (a < DEPTH) begin
|
||||||
|
|
||||||
|
dq_oe = 1'b1;
|
||||||
|
|
||||||
|
if (!lb_n && !ub_n)
|
||||||
|
dq_out = mem[a];
|
||||||
|
|
||||||
|
else if (!lb_n)
|
||||||
|
dq_out = {
|
||||||
|
8'h00,
|
||||||
|
mem[a][7:0]
|
||||||
|
};
|
||||||
|
|
||||||
|
else if (!ub_n)
|
||||||
|
dq_out = {
|
||||||
|
mem[a][15:8],
|
||||||
|
8'h00
|
||||||
|
};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// CE# FALLING
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(negedge ce_n) begin
|
||||||
|
|
||||||
|
if (!zz_n) begin
|
||||||
|
$display("ERROR: CE# LOW while ZZ# LOW");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
ce_low_time = $realtime;
|
||||||
|
|
||||||
|
addr_valid_time = $realtime;
|
||||||
|
active_addr = a;
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// READ START
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
if (!oe_n && we_n) begin
|
||||||
|
|
||||||
|
if (($realtime - powerup_time) < TPU_NS) begin
|
||||||
|
$display("ERROR: READ before tPU");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (last_read_start >= 0.0) begin
|
||||||
|
|
||||||
|
if (($realtime - last_read_start) < TRC_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tRC violation");
|
||||||
|
$display("required = %0.2f ns", TRC_NS);
|
||||||
|
$display("actual = %0.2f ns",
|
||||||
|
$realtime - last_read_start);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
last_read_start = $realtime;
|
||||||
|
|
||||||
|
read_active = 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// WRITE START
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
if (oe_n && !we_n) begin
|
||||||
|
|
||||||
|
if (($realtime - powerup_time) < TPU_NS) begin
|
||||||
|
$display("ERROR: WRITE before tPU");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (last_write_start >= 0.0) begin
|
||||||
|
|
||||||
|
if (($realtime - last_write_start) < TWC_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tWC violation");
|
||||||
|
$display("required = %0.2f ns", TWC_NS);
|
||||||
|
$display("actual = %0.2f ns",
|
||||||
|
$realtime - last_write_start);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
last_write_start = $realtime;
|
||||||
|
|
||||||
|
write_active = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// CE# RISING
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge ce_n) begin
|
||||||
|
|
||||||
|
realtime access_time;
|
||||||
|
|
||||||
|
ce_high_time = $realtime;
|
||||||
|
|
||||||
|
access_time = $realtime - ce_low_time;
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// READ END
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
if (read_active) begin
|
||||||
|
|
||||||
|
// tAA
|
||||||
|
if (access_time < TAA_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tAA violation");
|
||||||
|
$display("required = %0.2f ns", TAA_NS);
|
||||||
|
$display("actual = %0.2f ns", access_time);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
// tOE
|
||||||
|
if (($realtime - oe_low_time) < TOE_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tOE violation");
|
||||||
|
$display("required = %0.2f ns", TOE_NS);
|
||||||
|
$display("actual = %0.2f ns",
|
||||||
|
$realtime - oe_low_time);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
read_active = 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// OE# FALLING
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(negedge oe_n) begin
|
||||||
|
|
||||||
|
if (!zz_n) begin
|
||||||
|
$display("ERROR: OE# LOW while ZZ# LOW");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (!ce_n && we_n)
|
||||||
|
oe_low_time = $realtime;
|
||||||
|
|
||||||
|
// Illegal combination
|
||||||
|
if (!ce_n && !we_n) begin
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM PROTOCOL ERROR");
|
||||||
|
$display("OE# and WE# LOW simultaneously");
|
||||||
|
$display("========================================");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// OE# RISING
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge oe_n) begin
|
||||||
|
|
||||||
|
oe_high_time = $realtime;
|
||||||
|
|
||||||
|
// Output must remain valid long enough for tOH
|
||||||
|
// after address changes. This is checked by the
|
||||||
|
// controller access window rather than by forcing
|
||||||
|
// an artificial delay into the functional model.
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// WE# FALLING
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(negedge we_n) begin
|
||||||
|
|
||||||
|
if (!zz_n) begin
|
||||||
|
$display("ERROR: WE# LOW while ZZ# LOW");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (!ce_n && oe_n) begin
|
||||||
|
|
||||||
|
we_low_time = $realtime;
|
||||||
|
|
||||||
|
active_addr = a;
|
||||||
|
active_wdata = dq;
|
||||||
|
|
||||||
|
write_active = 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Illegal combination
|
||||||
|
if (!ce_n && !oe_n) begin
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM PROTOCOL ERROR");
|
||||||
|
$display("OE# and WE# LOW simultaneously");
|
||||||
|
$display("========================================");
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// WE# RISING
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(posedge we_n) begin
|
||||||
|
|
||||||
|
realtime write_width;
|
||||||
|
realtime data_setup;
|
||||||
|
|
||||||
|
we_high_time = $realtime;
|
||||||
|
|
||||||
|
if (write_active) begin
|
||||||
|
|
||||||
|
write_width = $realtime - we_low_time;
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// tWP
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
if (write_width < TWP_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tWP violation");
|
||||||
|
$display("required = %0.2f ns", TWP_NS);
|
||||||
|
$display("actual = %0.2f ns", write_width);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// tAW
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
if (($realtime - addr_valid_time) < TAW_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tAW violation");
|
||||||
|
$display("required = %0.2f ns", TAW_NS);
|
||||||
|
$display("actual = %0.2f ns",
|
||||||
|
$realtime - addr_valid_time);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// tCW
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
if (($realtime - ce_low_time) < TCW_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tCW violation");
|
||||||
|
$display("required = %0.2f ns", TCW_NS);
|
||||||
|
$display("actual = %0.2f ns",
|
||||||
|
$realtime - ce_low_time);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// tDW
|
||||||
|
//
|
||||||
|
// Data must be valid before WE# rises.
|
||||||
|
// Our controller drives DQ from WE# falling,
|
||||||
|
// therefore setup is much larger than 23 ns.
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
data_setup = $realtime - we_low_time;
|
||||||
|
|
||||||
|
if (data_setup < TDW_NS) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM TIMING ERROR");
|
||||||
|
$display("tDW violation");
|
||||||
|
$display("required = %0.2f ns", TDW_NS);
|
||||||
|
$display("actual = %0.2f ns", data_setup);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// tDH = 0 ns
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
// No additional hold time is required.
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Store data
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
if (a !== active_addr) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM PROTOCOL ERROR");
|
||||||
|
$display("ADDRESS CHANGED DURING WRITE");
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (a < DEPTH) begin
|
||||||
|
|
||||||
|
if (!lb_n)
|
||||||
|
mem[a][7:0] <= dq[7:0];
|
||||||
|
|
||||||
|
if (!ub_n)
|
||||||
|
mem[a][15:8] <= dq[15:8];
|
||||||
|
end
|
||||||
|
|
||||||
|
write_active = 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// ZZ#
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
always @(negedge zz_n) begin
|
||||||
|
|
||||||
|
if (!ce_n) begin
|
||||||
|
|
||||||
|
$display("");
|
||||||
|
$display("========================================");
|
||||||
|
$display("PSRAM PROTOCOL ERROR");
|
||||||
|
$display("ZZ# LOW while CE# LOW");
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
$fatal;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
File diff suppressed because one or more lines are too long
+10863
File diff suppressed because it is too large
Load Diff
+44888
File diff suppressed because one or more lines are too long
@@ -0,0 +1,78 @@
|
|||||||
|
module top (
|
||||||
|
input clk,
|
||||||
|
input rst,
|
||||||
|
input start,
|
||||||
|
|
||||||
|
output signed [31:0] y_bus,
|
||||||
|
output busy,
|
||||||
|
output done
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam DATA_WIDTH = 8;
|
||||||
|
localparam N_INPUTS = 256;
|
||||||
|
localparam N_NEURONS = 4;
|
||||||
|
localparam PARALLEL = 2;
|
||||||
|
localparam ACC_WIDTH = 32;
|
||||||
|
|
||||||
|
reg signed [DATA_WIDTH*N_INPUTS-1:0] x_bus;
|
||||||
|
reg signed [DATA_WIDTH*N_INPUTS*N_NEURONS-1:0] weights_bus;
|
||||||
|
reg signed [DATA_WIDTH*N_NEURONS-1:0] bias_bus;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
x_bus = 0;
|
||||||
|
weights_bus = 0;
|
||||||
|
bias_bus = 0;
|
||||||
|
|
||||||
|
// x[i] = 1
|
||||||
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
|
x_bus[i*DATA_WIDTH +: DATA_WIDTH] = 8'sd1;
|
||||||
|
|
||||||
|
// Neurone 0: primi 32 pesi = +1
|
||||||
|
for (i = 0; i < 32; i = i + 1)
|
||||||
|
weights_bus[
|
||||||
|
0*N_INPUTS*DATA_WIDTH +
|
||||||
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
|
] = 8'sd1;
|
||||||
|
|
||||||
|
// Neurone 1: tutti i pesi = 0, bias = 10
|
||||||
|
bias_bus[1*DATA_WIDTH +: DATA_WIDTH] = 8'sd10;
|
||||||
|
|
||||||
|
// Neurone 2: tutti i pesi = -1
|
||||||
|
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||||
|
weights_bus[
|
||||||
|
2*N_INPUTS*DATA_WIDTH +
|
||||||
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
|
] = -8'sd1;
|
||||||
|
|
||||||
|
// Neurone 3: primi 32 pesi = +4
|
||||||
|
for (i = 0; i < 32; i = i + 1)
|
||||||
|
weights_bus[
|
||||||
|
3*N_INPUTS*DATA_WIDTH +
|
||||||
|
i*DATA_WIDTH +:
|
||||||
|
DATA_WIDTH
|
||||||
|
] = 8'sd4;
|
||||||
|
end
|
||||||
|
|
||||||
|
layer #(
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.N_INPUTS(N_INPUTS),
|
||||||
|
.N_NEURONS(N_NEURONS),
|
||||||
|
.PARALLEL(PARALLEL),
|
||||||
|
.ACC_WIDTH(ACC_WIDTH)
|
||||||
|
) u_layer (
|
||||||
|
.clk(clk),
|
||||||
|
.rst(rst),
|
||||||
|
.start(start),
|
||||||
|
.x_bus(x_bus),
|
||||||
|
.weights_bus(weights_bus),
|
||||||
|
.bias_bus(bias_bus),
|
||||||
|
.y_bus(y_bus),
|
||||||
|
.busy(busy),
|
||||||
|
.done(done)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user