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 #(
|
||||
parameter DATA_WIDTH = 16,
|
||||
parameter FRAC_BITS = 8,
|
||||
parameter N_INPUTS = 64,
|
||||
parameter N_NEURONS = 8,
|
||||
parameter PARALLEL = 8,
|
||||
@@ -36,7 +35,6 @@ module layer #(
|
||||
|
||||
neuron_parallel #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.FRAC_BITS(FRAC_BITS),
|
||||
.N_INPUTS(N_INPUTS),
|
||||
.PARALLEL(PARALLEL),
|
||||
.ACC_WIDTH(ACC_WIDTH)
|
||||
|
||||
+89
-94
@@ -1,122 +1,117 @@
|
||||
module mac8 #(
|
||||
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*8-1:0] w_bus,
|
||||
input signed [DATA_WIDTH*PARALLEL-1:0] x_bus,
|
||||
input signed [DATA_WIDTH*PARALLEL-1:0] w_bus,
|
||||
input signed [ACC_WIDTH-1:0] acc_in,
|
||||
output signed [ACC_WIDTH-1:0] acc_out
|
||||
);
|
||||
|
||||
wire signed [ACC_WIDTH-1:0] m0;
|
||||
wire signed [ACC_WIDTH-1:0] m1;
|
||||
wire signed [ACC_WIDTH-1:0] m2;
|
||||
wire signed [ACC_WIDTH-1:0] m3;
|
||||
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;
|
||||
/*
|
||||
* Each MAC produces one sign-extended product.
|
||||
*/
|
||||
wire signed [ACC_WIDTH-1:0] products [0:PARALLEL-1];
|
||||
|
||||
wire signed [ACC_WIDTH-1:0] s0;
|
||||
wire signed [ACC_WIDTH-1:0] s1;
|
||||
wire signed [ACC_WIDTH-1:0] s2;
|
||||
wire signed [ACC_WIDTH-1:0] s3;
|
||||
genvar i;
|
||||
|
||||
wire signed [ACC_WIDTH-1:0] s4;
|
||||
wire signed [ACC_WIDTH-1:0] s5;
|
||||
|
||||
wire signed [ACC_WIDTH-1:0] sum;
|
||||
generate
|
||||
for (i = 0; i < PARALLEL; i = i + 1) begin : GEN_MAC
|
||||
|
||||
mac_unit #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ACC_WIDTH(ACC_WIDTH)
|
||||
) mac0 (
|
||||
.x(x_bus[0*DATA_WIDTH +: DATA_WIDTH]),
|
||||
.w(w_bus[0*DATA_WIDTH +: DATA_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(m0)
|
||||
.acc_out(products[i])
|
||||
);
|
||||
|
||||
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)
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
mac_unit #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ACC_WIDTH(ACC_WIDTH)
|
||||
) mac2 (
|
||||
.x(x_bus[2*DATA_WIDTH +: DATA_WIDTH]),
|
||||
.w(w_bus[2*DATA_WIDTH +: DATA_WIDTH]),
|
||||
.acc_in({ACC_WIDTH{1'b0}}),
|
||||
.acc_out(m2)
|
||||
);
|
||||
|
||||
mac_unit #(
|
||||
.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)
|
||||
);
|
||||
/*
|
||||
* Balanced binary adder tree.
|
||||
*
|
||||
* PARALLEL is intended to be a power of two:
|
||||
* 8 -> 3 levels
|
||||
* 16 -> 4 levels
|
||||
* 32 -> 5 levels
|
||||
*
|
||||
* 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 #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.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)
|
||||
);
|
||||
localparam TREE_LEVELS = $clog2(PARALLEL);
|
||||
|
||||
mac_unit #(
|
||||
.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)
|
||||
);
|
||||
wire signed [ACC_WIDTH-1:0]
|
||||
tree [0:TREE_LEVELS][0:PARALLEL-1];
|
||||
|
||||
mac_unit #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ACC_WIDTH(ACC_WIDTH)
|
||||
) mac6 (
|
||||
.x(x_bus[6*DATA_WIDTH +: DATA_WIDTH]),
|
||||
.w(w_bus[6*DATA_WIDTH +: DATA_WIDTH]),
|
||||
.acc_in({ACC_WIDTH{1'b0}}),
|
||||
.acc_out(m6)
|
||||
);
|
||||
generate
|
||||
|
||||
mac_unit #(
|
||||
.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)
|
||||
);
|
||||
/*
|
||||
* Level 0 = individual products
|
||||
*/
|
||||
for (i = 0; i < PARALLEL; i = i + 1) begin : GEN_TREE_INPUT
|
||||
assign tree[0][i] = products[i];
|
||||
end
|
||||
|
||||
assign s0 = m0 + m1;
|
||||
assign s1 = m2 + m3;
|
||||
assign s2 = m4 + m5;
|
||||
assign s3 = m6 + m7;
|
||||
endgenerate
|
||||
|
||||
assign s4 = s0 + s1;
|
||||
assign s5 = s2 + s3;
|
||||
|
||||
assign sum = s4 + s5;
|
||||
genvar level;
|
||||
genvar node;
|
||||
|
||||
assign acc_out = acc_in + sum;
|
||||
generate
|
||||
|
||||
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
|
||||
@@ -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 #(
|
||||
parameter DATA_WIDTH = 16,
|
||||
parameter FRAC_BITS = 8,
|
||||
parameter N_INPUTS = 64,
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter N_INPUTS = 32,
|
||||
parameter PARALLEL = 8,
|
||||
parameter ACC_WIDTH = 40
|
||||
parameter ACC_WIDTH = 32
|
||||
)(
|
||||
input clk,
|
||||
input rst,
|
||||
@@ -31,23 +30,25 @@ module neuron_parallel #(
|
||||
|
||||
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] final_acc;
|
||||
wire signed [ACC_WIDTH-1:0] final_value;
|
||||
|
||||
assign x_group =
|
||||
x_bus[group_index*PARALLEL*DATA_WIDTH
|
||||
+: PARALLEL*DATA_WIDTH];
|
||||
x_bus[
|
||||
group_index*PARALLEL*DATA_WIDTH
|
||||
+: PARALLEL*DATA_WIDTH
|
||||
];
|
||||
|
||||
assign w_group =
|
||||
w_bus[group_index*PARALLEL*DATA_WIDTH
|
||||
+: PARALLEL*DATA_WIDTH];
|
||||
w_bus[
|
||||
group_index*PARALLEL*DATA_WIDTH
|
||||
+: PARALLEL*DATA_WIDTH
|
||||
];
|
||||
|
||||
mac8 #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ACC_WIDTH(ACC_WIDTH)
|
||||
.ACC_WIDTH(ACC_WIDTH),
|
||||
.PARALLEL(PARALLEL)
|
||||
) u_mac8 (
|
||||
.x_bus(x_group),
|
||||
.w_bus(w_group),
|
||||
@@ -55,17 +56,12 @@ module neuron_parallel #(
|
||||
.acc_out(acc_next)
|
||||
);
|
||||
|
||||
assign bias_ext_small = bias;
|
||||
|
||||
// Sign extension INT8 -> INT32
|
||||
assign bias_ext =
|
||||
{{(ACC_WIDTH-DATA_WIDTH){bias_ext_small[DATA_WIDTH-1]}},
|
||||
bias_ext_small};
|
||||
{{(ACC_WIDTH-DATA_WIDTH){bias[DATA_WIDTH-1]}}, bias};
|
||||
|
||||
assign final_acc =
|
||||
acc_next + (bias_ext <<< FRAC_BITS);
|
||||
|
||||
assign final_value =
|
||||
final_acc >>> FRAC_BITS;
|
||||
// Accumulazione finale + bias
|
||||
assign final_acc = acc_next + bias_ext;
|
||||
|
||||
always @(posedge clk) begin
|
||||
|
||||
@@ -93,14 +89,18 @@ module neuron_parallel #(
|
||||
|
||||
acc <= final_acc;
|
||||
|
||||
if (final_value <= 0) begin
|
||||
// ReLU
|
||||
if (final_acc <= 0) begin
|
||||
y <= 0;
|
||||
end
|
||||
else if (final_value > 32767) begin
|
||||
y <= 16'sh7FFF;
|
||||
|
||||
// Saturazione INT32 -> INT8
|
||||
else if (final_acc > 127) begin
|
||||
y <= 8'sd127;
|
||||
end
|
||||
|
||||
else begin
|
||||
y <= final_value[DATA_WIDTH-1:0];
|
||||
y <= final_acc[DATA_WIDTH-1:0];
|
||||
end
|
||||
|
||||
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
+65
-159
@@ -2,12 +2,11 @@
|
||||
|
||||
module tb;
|
||||
|
||||
parameter DATA_WIDTH = 16;
|
||||
parameter FRAC_BITS = 8;
|
||||
parameter N_INPUTS = 32;
|
||||
parameter DATA_WIDTH = 8;
|
||||
parameter N_INPUTS = 256;
|
||||
parameter N_NEURONS = 4;
|
||||
parameter PARALLEL = 8;
|
||||
parameter ACC_WIDTH = 40;
|
||||
parameter PARALLEL = 32;
|
||||
parameter ACC_WIDTH = 32;
|
||||
|
||||
reg clk;
|
||||
reg rst;
|
||||
@@ -21,20 +20,17 @@ module tb;
|
||||
reg signed [DATA_WIDTH*N_NEURONS-1:0]
|
||||
bias_bus;
|
||||
|
||||
wire signed [DATA_WIDTH*N_NEURONS-1:0]
|
||||
y_bus;
|
||||
wire signed [DATA_WIDTH*N_NEURONS-1:0] y_bus;
|
||||
|
||||
wire busy;
|
||||
wire done;
|
||||
|
||||
integer i;
|
||||
integer n;
|
||||
integer errors;
|
||||
integer expected;
|
||||
|
||||
layer #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.FRAC_BITS(FRAC_BITS),
|
||||
.N_INPUTS(N_INPUTS),
|
||||
.N_NEURONS(N_NEURONS),
|
||||
.PARALLEL(PARALLEL),
|
||||
@@ -56,13 +52,6 @@ module tb;
|
||||
forever #5 clk = ~clk;
|
||||
end
|
||||
|
||||
function signed [15:0] q8_8;
|
||||
input real value;
|
||||
begin
|
||||
q8_8 = $rtoi(value * 256.0);
|
||||
end
|
||||
endfunction
|
||||
|
||||
task run_layer;
|
||||
begin
|
||||
@(posedge clk);
|
||||
@@ -94,107 +83,81 @@ module tb;
|
||||
rst = 0;
|
||||
|
||||
/*
|
||||
* All inputs = 1.0
|
||||
* Tutti gli input = 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
|
||||
* result = N_INPUTS
|
||||
* N0:
|
||||
* primi 32 pesi = 1
|
||||
* restanti 224 = 0
|
||||
*
|
||||
* Risultato = 32
|
||||
*
|
||||
* Questo verifica che l'accumulatore
|
||||
* attraversi correttamente tutti gli 8 gruppi.
|
||||
*/
|
||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||
weights_bus[
|
||||
0*N_INPUTS*DATA_WIDTH +
|
||||
i*DATA_WIDTH +:
|
||||
DATA_WIDTH
|
||||
] = q8_8(1.0);
|
||||
] = (i < 32) ? 8'sd1 : 8'sd0;
|
||||
|
||||
/*
|
||||
* Neuron 1: weight = 0.5
|
||||
* result = N_INPUTS / 2
|
||||
* N1:
|
||||
* tutti i 256 pesi = 0
|
||||
* bias = 10
|
||||
*
|
||||
* Risultato = 10
|
||||
*/
|
||||
if (N_NEURONS > 1)
|
||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||
weights_bus[
|
||||
1*N_INPUTS*DATA_WIDTH +
|
||||
i*DATA_WIDTH +:
|
||||
DATA_WIDTH
|
||||
] = q8_8(0.5);
|
||||
] = 8'sd0;
|
||||
|
||||
bias_bus[1*DATA_WIDTH +: DATA_WIDTH] = 8'sd10;
|
||||
|
||||
/*
|
||||
* Neuron 2: weight = -0.5
|
||||
* N2:
|
||||
* tutti i pesi = -1
|
||||
*
|
||||
* Risultato = -256
|
||||
* 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);
|
||||
] = -8'sd1;
|
||||
|
||||
/*
|
||||
* Neuron 3: weight = 2.0
|
||||
* Large positive result -> saturation
|
||||
* N3:
|
||||
* primi 32 pesi = 4
|
||||
* restanti = 0
|
||||
*
|
||||
* Risultato = 128
|
||||
* Saturazione INT8 -> 127
|
||||
*/
|
||||
if (N_NEURONS > 3)
|
||||
for (i = 0; i < N_INPUTS; i = i + 1)
|
||||
weights_bus[
|
||||
3*N_INPUTS*DATA_WIDTH +
|
||||
i*DATA_WIDTH +:
|
||||
DATA_WIDTH
|
||||
] = q8_8(2.0);
|
||||
|
||||
/*
|
||||
* Neuron 4: zero weights + bias +1
|
||||
*/
|
||||
if (N_NEURONS > 4)
|
||||
bias_bus[4*DATA_WIDTH +: DATA_WIDTH] = q8_8(1.0);
|
||||
|
||||
/*
|
||||
* Neuron 5: zero weights + bias -1
|
||||
* ReLU -> 0
|
||||
*/
|
||||
if (N_NEURONS > 5)
|
||||
bias_bus[5*DATA_WIDTH +: DATA_WIDTH] = q8_8(-1.0);
|
||||
|
||||
/*
|
||||
* 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
|
||||
] = (i < 32) ? 8'sd4 : 8'sd0;
|
||||
|
||||
$display("");
|
||||
$display("========================================");
|
||||
$display("PARAMETRIC LAYER TEST");
|
||||
$display("INT8 / INT32 PARAMETRIC LAYER TEST");
|
||||
$display("N_INPUTS = %0d", N_INPUTS);
|
||||
$display("N_NEURONS = %0d", N_NEURONS);
|
||||
$display("PARALLEL = %0d", PARALLEL);
|
||||
$display("DATA_WIDTH = %0d", DATA_WIDTH);
|
||||
$display("ACC_WIDTH = %0d", ACC_WIDTH);
|
||||
$display("========================================");
|
||||
|
||||
run_layer;
|
||||
@@ -202,40 +165,45 @@ module tb;
|
||||
/*
|
||||
* 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",
|
||||
y_bus[0*16 +: 16], expected);
|
||||
$signed(y_bus[0*DATA_WIDTH +: DATA_WIDTH]),
|
||||
expected);
|
||||
errors = errors + 1;
|
||||
end
|
||||
else
|
||||
$display("PASS N0: %0d", y_bus[0*16 +: 16]);
|
||||
$display("PASS N0: %0d", $signed(y_bus[0*DATA_WIDTH +: DATA_WIDTH]));
|
||||
|
||||
/*
|
||||
* Neuron 1
|
||||
*/
|
||||
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",
|
||||
y_bus[1*16 +: 16], expected);
|
||||
$signed(y_bus[1*DATA_WIDTH +: DATA_WIDTH]),
|
||||
expected);
|
||||
errors = errors + 1;
|
||||
end
|
||||
else
|
||||
$display("PASS N1: %0d", y_bus[1*16 +: 16]);
|
||||
$display("PASS N1: %0d",
|
||||
$signed(y_bus[1*DATA_WIDTH +: DATA_WIDTH]));
|
||||
end
|
||||
|
||||
/*
|
||||
* Neuron 2
|
||||
*/
|
||||
if (N_NEURONS > 2) begin
|
||||
|
||||
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",
|
||||
y_bus[2*16 +: 16]);
|
||||
$signed(y_bus[2*DATA_WIDTH +: DATA_WIDTH]));
|
||||
errors = errors + 1;
|
||||
end
|
||||
else
|
||||
@@ -243,86 +211,24 @@ module tb;
|
||||
end
|
||||
|
||||
/*
|
||||
* Neuron 3:
|
||||
* weight = 2.0
|
||||
* result = N_INPUTS * 2.0
|
||||
* Neuron 3
|
||||
*
|
||||
* Saturation only occurs when result > 127.996...
|
||||
* 32 * 4 = 128
|
||||
* INT8 positive saturation -> 127
|
||||
*/
|
||||
if (N_NEURONS > 3) begin
|
||||
|
||||
expected = N_INPUTS * 2 * 256;
|
||||
expected = 127;
|
||||
|
||||
if (expected > 32767)
|
||||
expected = 32767;
|
||||
|
||||
if (y_bus[3*16 +: 16] !== expected[15:0]) begin
|
||||
if ($signed(y_bus[3*DATA_WIDTH +: DATA_WIDTH]) !== expected) begin
|
||||
$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]);
|
||||
$signed(y_bus[3*DATA_WIDTH +: DATA_WIDTH]),
|
||||
expected);
|
||||
errors = errors + 1;
|
||||
end
|
||||
else
|
||||
$display("PASS N4: 256 (bias)");
|
||||
end
|
||||
|
||||
/*
|
||||
* 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]);
|
||||
$display("PASS N3: %0d (saturation)",
|
||||
$signed(y_bus[3*DATA_WIDTH +: DATA_WIDTH]));
|
||||
end
|
||||
|
||||
if (busy !== 0) begin
|
||||
@@ -339,9 +245,9 @@ module tb;
|
||||
$display("========================================");
|
||||
|
||||
if (errors == 0)
|
||||
$display("PARAMETRIC TEST PASSED");
|
||||
$display("INT8 PARAMETRIC TEST PASSED");
|
||||
else
|
||||
$display("PARAMETRIC TEST FAILED: %0d errors", errors);
|
||||
$display("INT8 PARAMETRIC TEST FAILED: %0d errors", errors);
|
||||
|
||||
$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