Luigi Loreti

FPGA · Progettazione a blocchi · Verilog

Vent’anni di progetti
con le FPGA

Dai blocchi funzionali al controllo di un DMD.

Un esempio del mio metodo di lavoro: architettura gerarchica, schemi e codice, dalla sperimentazione su DE10-Standard alla scheda proprietaria.

Un racconto in prima persona

Il filo conduttore dei miei ultimi vent’anni

La progettazione con FPGA è stata il filo conduttore del mio lavoro negli ultimi vent’anni. Ho lavorato soprattutto con Altera, ma ho utilizzato anche dispositivi Xilinx e l’ambiente Vivado. La mia preferenza è rimasta per gli strumenti Altera: prima MAX+PLUS II, poi Quartus.

Pensare per blocchi, scendere fino al codice

Il mio approccio è sempre stato quello di vedere il progetto come un insieme di blocchi funzionali. Ogni blocco poteva contenerne altri, in una gerarchia che permetteva di passare dall’architettura generale ai dettagli dell’implementazione.

Alla fine di quel percorso c’era il codice: in questo esempio Verilog, ma nei miei progetti ho utilizzato anche VHDL. Schemi e linguaggi non erano due modi di lavorare contrapposti: erano livelli diversi dello stesso progetto.

Con le FPGA ho realizzato una grande varietà di sistemi. Questo esempio mostra un controller per un DMD della Texas Instruments, destinato a un sistema di visualizzazione a 30.000 frame al secondo.

La scheda DE10-Standard

La piattaforma di sviluppo utilizzata per controllare e sperimentare il sistema prima del trasferimento sulla scheda proprietaria.

Scheda Terasic DE10-Standard vista dall’alto, con indicazioni di FPGA, HPS, memorie, connettori e periferiche.
DE10-Standard, vista dall’alto con i principali componenti identificati. Immagine tratta dalla figura 2-1 del manuale della scheda, fornita dall’autore. Seleziona la fotografia per ingrandirla.
Caratteristiche della piattaforma
FPGA SoCCyclone V SX — 5CSXFC6D6F31C6N
Logica programmabile110K elementi logici; 41.509 ALM
Memoria integrata5.761 Kbit
Clocking6 PLL FPGA e 3 PLL HPS
Processore HPSARM Cortex-A9 MPCore dual-core a 925 MHz
Cache e RAM HPS512 KB di cache L2 condivisa; 64 KB di scratch RAM
Uscita videoVGA con DAC a 24 bit
Ingresso videoDecoder TV NTSC/PAL/SECAM e connettore TV-In
EspansioniGPIO a 40 pin, HSMC, ingresso ADC e connettore LTC
ComunicazioniGigabit Ethernet; 2 porte USB 2.0 host; USB-UART sul lato HPS
AudioCodec a 24 bit, line-in, line-out e ingresso microfono
Conversione analogicaADC a 12 bit, 8 canali, 500 kSPS; intervallo 0–4,096 V
ProgrammazioneUSB-Blaster II integrato; memoria di configurazione EPCS128
Interfaccia utente10 interruttori, 5 tasti utente, 11 LED e 6 display a 7 segmenti; LCD 128 × 64 sul lato HPS
Alimentazione12 V DC

Fonte delle specifiche: Terasic — DE10-Standard Development and Education Kit. La scheda integra logica FPGA e sistema processore HPS: non tutte le periferiche sono collegate allo stesso dominio. L’elenco descrive le dotazioni della piattaforma, non implica che siano tutte impiegate nel progetto.

I 30.000 frame/s riguardano il sistema DMD descritto dall’autore: non sono la frequenza di aggiornamento dell’uscita VGA della DE10-Standard.

Un controller DMD su Cyclone V

Dalla piattaforma di sperimentazione alla realizzazione su scheda proprietaria.

Scheda di sviluppoDE10-Standard
FPGAAltera Cyclone V
Sistema descritto dall’autore30.000 frame/s

La scheda di controllo e sperimentazione era una DE10-Standard con FPGA Cyclone V. Dopo questa fase, il progetto venne trasferito su una scheda proprietaria.

SPV5.bdf — vista dei collegamenti e dei blocchi del progetto.
SPV5.bdf — vista dei collegamenti e dei blocchi del progetto. Seleziona l’immagine per ingrandirla.
SPVsimple.bdf — organizzazione gerarchica dei blocchi e delle interfacce.
SPVsimple.bdf — organizzazione gerarchica dei blocchi e delle interfacce. Seleziona l’immagine per ingrandirla.
SPV6_VGA_DLP_VGA.bdf — una vista dei moduli e delle connessioni.
SPV6_VGA_DLP_VGA.bdf — una vista dei moduli e delle connessioni. Seleziona l’immagine per ingrandirla.

Questa pagina documenta il progetto attraverso il racconto dell’autore, tre schermate dell’ambiente e un modulo sorgente. Il valore di 30.000 frame/s si riferisce al sistema descritto: non è una prestazione verificata dalla sola pubblicazione di questo codice.

Il sorgente Verilog completo

Modulo DLP_coamd_top, riportato integralmente come fornito dall’autore, senza correzioni automatiche. L’intestazione del sorgente indica Quartus Prime Lite 20.1.1 e la data del 23 maggio 2023.

È uno dei moduli del progetto, non un progetto Quartus autonomo e completo. Il sorgente è presentato come documento originale: non è stato compilato o validato in questa pagina.

// PROGRAM"Quartus Prime"
// VERSION"Version 20.1.1 Build 720 11/11/2020 SJ Lite Edition"
// CREATED"Tue May 23 07:20:38 2023"

module DLP_coamd_top(

Clk_400Mhz,
Gen_reset,
Mem_write_add_in,
RST_ACTIVE,
INT_ACTIVE,
ECP2_Finished,
command,
DMD_TYPE,
VERSION,
Cloc_mem,
DLP_Data_valid,
ARSTZ,
COMP_DATA,
LOAD4,
DMD_power_float,
RST2BLKZ,
DLP_NS_flip,
DLP_block_mode,
DLP_Bock_add,
DLP_Row_add,
DLP_Row_mode,
DLP_Sel,
DLP_status,
Row_line,
h_cnt,
D_in,
A_bus,
A_clk,
A_dvalid,
B_bus,
B_clk,
B_dvalid,
C_bus,
C_clk,
C_dvalid,
D_bus,
D_clk,
D_dvalid
);


input wireGen_reset;
input wireClk_400Mhz;
output wireCloc_mem;

// Data from VGA memory
input wire[255:0] D_in;
input wire [15:0] Mem_write_add_in;



reg[63:0] MUX;
wire[255:0] D_inM;
reg  [255:0] Din;

//input from DLP
input wireRST_ACTIVE;
input wireINT_ACTIVE;
input wireECP2_Finished;
input wire[3:0] DMD_TYPE;
input wire[2:0] VERSION;

// output to DLP

output reg[1:0] DLP_block_mode;
output reg[3:0] DLP_Bock_add;
output reg[10:0] DLP_Row_add;
output reg[1:0] DLP_Row_mode;
output regDLP_Data_valid;
output regARSTZ;
output regCOMP_DATA;
output regLOAD4;
output regDMD_power_float;
output regRST2BLKZ;
output regDLP_NS_flip;

output wire [31:0] A_bus;
output wire[31:0] B_bus;
output wire [31:0] C_bus;
output wire[31:0] D_bus;

output wire A_dvalid;
output wire A_clk;
output wire B_dvalid;
output wire B_clk;
output wire C_dvalid;
output wire C_clk;
output wire D_dvalid;
output wire D_clk;

assign A_bus=MUX[31:0];
assign B_bus=MUX[63:32];
assign C_bus=MUX[31:0];
assign D_bus=MUX[63:32];




output reg[9:0]  DLP_status;
input wire[15:0] command;

//
//ARSTZ,
//COMP_DATA,
//LOAD4,
//DMD_power_float,
//RST2BLKZ,
//DLP_NS_flip,
//DLP_block_mode,
//DLP_Bock_add,
//DLP_Row_add,
//DLP_Row_mode,
//DLP_Sel,
//DLP_status,
//Row_lin



output reg[3:0] DLP_Sel;
output reg Row_line;



// DLP state machine

output reg [6:0] h_cnt;

wire [6:0] START_SEQ=10;
wire [6:0] INIT = START_SEQ+1; 
wire [6:0] START =START_SEQ+2;
wire [6:0] TOTAL_LEN=70;
wire [6:0] START1= START +17;
wire [6:0] START2= START1+17;
wire [6:0] START3= START2+17;
wire [6:0] START4= START3+17;
wire [6:0] START_BLOCK=START_SEQ+70;
wire [6:0] SEQ_LEN =15;



//reg[15:0] temp_data; 



wire enabl;

always@(negedge Clk_400Mhz or posedge RST_ACTIVE  )
    begin
        if(RST_ACTIVE) 
 h_cnt<=0;
        else

 h_cnt<=h_cnt+1;
    end



always @(posedge Clk_400Mhz)

begin


begin
DLP_Data_valid <=( h_cnt>START) &&  (h_cnt<START1) ||

(h_cnt>START1) &&  (h_cnt<START2) ||

(h_cnt>START2 &&  (h_cnt<START3)  ||
(h_cnt>START3) &&  (h_cnt<START4)) ?1'b1:1'b0;

if (DLP_Data_valid)
        begin
    DLP_Sel<=DLP_Sel+1;
end
else
DLP_Sel<=4'b0;
end






  case (h_cnt)

    START_SEQ: begin

DLP_block_mode[1:0]<='b0;
DLP_Bock_add<=0;
DLP_Row_add<=0;
DLP_Row_mode<=0;
DLP_Sel<=0;
DLP_Data_valid<='b0;
ARSTZ<=0;
COMP_DATA<=0;
LOAD4<=0;
DMD_power_float<=0;
RST2BLKZ<=0;
DLP_NS_flip<=0;
Row_line<=0;
    end

   INIT: begin
DLP_block_mode[1:0]<='b00;
Row_line<=1;
DLP_Row_add[9:2]<=Mem_write_add_in[15:8];
DLP_Row_mode<='b10;

end

START:
begin
Row_line<=0;
DLP_Bock_add<=(DLP_Row_add[9:4]/3);//4'b0;
 end

START1,START2,START3:

begin
//DLP_Data_valid<='b1;

//DLP_Sel<=DLP_Sel+1;
Row_line<=0;
DLP_Row_add<=DLP_Row_add+1;

          end
START_BLOCK:DLP_block_mode[1:0]<='b10;



default : begin 

DLP_block_mode[1:0]<='b00;
//DLP_Bock_add<=0;
//DLP_Row_add<=0;
DLP_Row_mode<=0;
//DLP_Sel<=0;
//DLP_Data_valid<='b0;
ARSTZ<=0;
COMP_DATA<=0;
LOAD4<=0;
DMD_power_float<=0;
RST2BLKZ<=0;
DLP_NS_flip<=0;
end

  endcase

end

always@(posedge Row_line )
 begin
      Din<=D_in;
end

integer i;


always@(posedge Clk_400Mhz)

begin
for(i=0;i<16;i=i+1)   // 16 gruppi da 4 bit = 63 bit
begin
        // extract 4 bit a times and apply LUT depending on data value
  // data= 0  pattern 0101 data = 1  attern 1010
  // riga rappresenta le righe èari o disèari nel DLP
  // se riga=0  e data 0 0 il pattern è 0101
   //   pattern per Data = 0   riga n     0101   Data=1 1010
//                             riga n+1   1010          0101 
//                             riga n+2   0101          1010
//                             riga n+3   1010          0101

MUX[(4*i)+:4]<=DLP_Row_add[0]?(Din[16*DLP_Sel+ i]? 4'b0101:4'b1010):(Din[16*DLP_Sel+ i]? 4'b1010:4'b0101);

end 
end

endmodule

Continua il percorso

PALASM, AHDL, VHDL e Verilog →

Il calcolo ottico e OptoPC →

Approfondimenti e materiali storici →

Luigi Loreti

Contatti

Per ricerca, collaborazioni, idee, musica, vela o semplice curiosità:

luigi.loreti@optopc.it
Vista annotata dei componenti e delle interfacce della DE10-Standard