CID Aplicatii 2

De la WikiLabs

Module de test - Introducere

Modulul de test este folosit pentru testarea circuitului descris, prin simulare, si nu este sintetizabil. Simularea permite detectia rapida a erorilor de implementare si corectarea acestora. Ideea generala a unui modul de test este descrisa in figura de mai jos (consideram un circuit cu numele circuit si modulul sau de test circuit_TB):

Circuit TB.PNG

Observatii:

  • Modulul de test NU are intrari sau iesiri si nu este sintetizabil. Toate procesele au loc in interiorul acestuia.
  • Modulul de test are in principal urmatoarele componente: o instantiere a modulului testat (circuit), un generator de semnale de test si, optional, un bloc de evaluare automata a rezultatului. Aceasta evaluare automata devine insa obligatorie pentru circuitele complexe.
  • Generatorul de semnale de test va genera cate un semnal pentru fiecare intrare a circuitului. Tinand cont ca aceasta generare se face in blocuri initial, trebuie sa definim cate un semnal de test de tip reg pentru fiecare intrare a modulului testat.
  • Pentru fiecare iesire a modulului testat, definim cate un semnal de tip wire. Acesta poate fi evaluat mai departe intr-un bloc de evaluare automata.

Notiuni de sintaxa

  • Variabilele de tip wire sunt folosite pentru a conecta elemente dintr-un design. Acestea pot fi citite sau asignate, insa nu pot stoca valori
  • Variabilele de tip reg sunt folosite pentru a stoca date.

Exemplul 1

Descrieti structural circuitul reprezentat mai jos si creati pentru acesta un modul de test.

Gates2.png

Implementarea Verilog a circuitului

module exemplul1(out, in0, in1, sel);

  output out;
  input in0;
  input in1;
  input sel;
  wire w1, w2, w3;

  not inverter_gate(w1, sel);
  and and_gate1(w2, in0, w1);
  and and_gate2(w3, sel, in1);
  or or_gate(out, w2, w3);

endmodule

Implementarea Verilog a modulului de test

`timescale 1ns/1ps

module exemplul1_TB();

  reg in0_t, in1_t, sel_t;
  wire out_t;
  integer idx;

  initial begin
    for(idx=0; idx<8;idx=idx+1) begin
      in0_t = idx[0];
      in1_t = idx[1];
      sel_t = idx[2];
      #1;
    end  
  end
  
  exemplul1DUT(out_t, in0_t, in1_t, sel_t);

endmodule

Notiuni de sintaxa

  • Tipul de date integer este reprezinta o variabila de tip reg definit pe 32 de biti (reg[31:0] )
  • Blocul initial se executa intotdeauna la timpul 0 al simularii. In cazul in care sunt definite mai multe blocuri initial, acestea se vor executa in paralel.
  • `timescale este o directiva de preprocesare care ii indica simulatorului unitatea default de masurarea a timpului si precizia pasului de simulare. In cazul de mai sus `timescale 1ns/1ps inseamna ca precizia de simulare este 1ps, iar unitate de masura a timpului este in ns.
  • Pentru a astepta un anumit interval de tip se pot folosi instructiuni de intarziere:
    • #1; - Asteapta o unitate de timp (conform celei specificate in directiva `timescale) Intarzierile cu "#" nu sunt recomandate in testarea circuitelor secventiale. Exemple:
    • #5ns; - Asteapta 5 nanosecunde
    • #12us; - Asteapta 12 microsecunde
    • #1s; - Asteapta o secunda

Observatii:

  • Modulul exemplul1 este implementat folosind instantieri ale portilor logice predefinite in Verilog.
  • Instantierea, in general, se face astfel: tip_modul nume_modul (...intefata...). In cazul nostru, interfatarea se face implicit prin ordine: punem semnalele la care modulul trebuie sa se conecteze in ordine si acestea se vor conecta automat. Pentru portile logice, interfata este de tipul (out, in0, in1, ....).
  • Acest mod de interfatare este oarecum acceptat pentru modulele foarte simple, dar nerecomandat pentru modulele complexe, unde gresirea ordinii duce la implementarea gresita a circuitelor.
  • Pentru modulul de test(exemplul1_TB), observam ca interfata acestuia este goala (nu exista intrari sau iesiri).
  • De asemenea, pentru fiecare intrare a modulului testat se defineste un semnal de tip reg, iar pentru fiecare iesire se defineste un semnal de tip wire. Atentie! Dimensiunea acestor semnale trebuie sa corespunda cu dimensiunea semnalelor din interfata modului la care vor fi legate.
  • Generarea semnalelor de test a fost facuta cu ajutorul unei bucle de tip for care variaza un index intre 0 si 7 (generam astfel toate variantele de biti pentru cele trei semnale de 1 bit: sel_t, in0_tsi in1_t, fiecare legandu-se la un bit al indexului). In instantierea modulului exemplu1 observam din nou interfatarea implicita prin ordine.
  • Analizand ordinea semnalelor din interfata modulului exemplul1 si ordinea legaturilor din instantierea acestuia in exemplul1_TB, out_t se va lega la out, sel_tla sel, in0_tla in0 si in1_t la in1.

Exemplul 2

Descrieti functional circuitul anterior cu ajutorul operatorilor Verilog si realizati un modul de test cu evaluare automata.

Implementarea Verilog a circuitului

module exemplul2(
  output out,
  input in0,
  input in1,
  input sel
);

  assign out = ((~sel) & in0) | (sel & in1);

endmodule

Implementarea Verilog a modulului de test

`timescale 1ns/1ps

module exemplul2_TB();

  reg in0_t, in1_t, sel_t;
  wire out_t;
  integer idx; //index for loop
  reg flag; //test status flag

  initial begin
    flag = 0;
    for(idx=0; idx<8;idx=idx+1)  begin
      in0_t = idx[0];
      in1_t = idx[1];
      sel_t = idx[2];
      #1;
      if(((sel_t == 0) && (out_t == in0_t)) || ((sel_t == 1) && (out_t == in1_t))) begin 
      end else begin
        flag = 1; 
        $display("TEST_FAIL at sel = %b, in0 = %b, in1 = %b, out = %b", sel_t, in0_t, in1_t, out_t);
      end 
    end
    
    if(flag == 0)
      $display("TEST_PASS");
  end
  
  exemplul2 dut(
    .in0(in0_t),
    .in1(in1_t),
    .sel(sel_t),
    .out(out_t));

endmodule

Elemente de sintaxa

  • Functia de sistem $display este folosita pentru a printa mesaje in consola. Aceasta are sintaxa similara cu function printf din C. Aceasta este nesintetizabila si nu ar trebui folosita in modulele proiectate decat pentru debug.

Observatii:

  • Pentru modulul de test: Pe langa generarea semnalelor de test, observam ca la fiecare pas testam corectitudinea iesirii. Asa cum am observat, circuitul realizeaza urmatoarea functie: atunci cand sel este 0, out va fi egal cu in0, iar atunci cand sel este 1, out va fi egal cu in1. Ne vom folosi de acest lucru pentru evaluarea automata.
  • Consideram un fanion flag, de tip reg deoarece isi va modifica valoarea intr-un bloc initial, pe care il vom initializa cu 0 (prezumtia de nevinovatie– consideram initial circuitul corect). Testam conditia de corectitudine la fiecare pas de generare a semnalelor de test (la fiecare iteratie a buclei for) si daca ea este indeplinita, nimic nu se intampla cu flag, ramanand 0. Daca acesta conditie nu este indeplinita cel putin o data, flag se va face 1, semnalizand ca apare cel putin un caz incorect, afisandu-se totodata in consola cu $display mesajul TEST_FAIL si starea semnalelor in momentul acestuia. La iesirea din bucla testam din nou fanionul si daca acesta este 0, inseamna ca nu a aparut nici macar un caz incorect, afisand in cazul acesta mesajul TEST_PASS.
  • Instantierea modulului testat se face astfel:
              .semnal_interfata_modul_testat(semnalul_la care_va_fi_conectat)

Aceasta modalitate ne ofera controlul asupra conexiunilor, fara constrangeri de ordine.

Exemplul 3

Descrieti comportamental acelasi circuit, folosind blocurile assign conditionat, always + if si always + case.

Implementarea Verilog a circuitului

  • Folosing assign conditionat
module exemplul3(
  output out,
  input in0,
  input in1, 
  input sel
  );
 
  assign out = (sel == 0) ? in0 : in1;

endmodule
  • Folosing always si if
module exemplul3(
  output out,
  input in0,
  input in1, 
  input sel
  );
 
  always@(in0 or in1 or sel) begin
    if(sel == 0)
      out = in0;
    else
      out = in1;
  end

endmodule
  • Folosing always si case
module exemplul3(
  output out,
  input in0,
  input in1, 
  input sel
  );
 
  always@(*) begin
    case(sel)
      1'b0: out = in0;
      1'b1: out = in1;
      default: out = in0;
    endcase
  end

endmodule

Elemente de sintaxa

  • Blocul always descrie un comportament repetitiv. Acesta se executa atunci cand se schimba un element din lista de senzitivitate "@(...)". In functie de semnalele definite in lista de senzitivitate, blocul always poate avea atat comportament combinational, cat si secvential (vezi Verilog pentru mai multe detalii).

Observatii:

  • In primul caz, assign conditionat are o sintaxa asemanatoare cu cea a limbajului C:
        (conditie) ? semnalul_cond_indeplinita : semnal_cond_neindeplinita;  
  • In cazul 2, folosind un bloc always pentru actualizarea semnalului out, acesta trebuie declarat ca reg (output reg out). In interiorul parantezelor blocului always punem lista de semnale la care acesta va fi sensibil. Fiind un circuit combinational, out trebuie sa fie sensibil imediat la toate semnalele care il influenteaza. Putem inlocui toata aceasta lista cu *. Asta ne asigura ca nu am omis niciun semnal.
  • In cazul 3, folosim * pentru lista de senzitivitati a circuitului si un bloc case. Atentie! Desi aici este clar ca am acoperit toate combinatiile posibile ale lui sel(0 si 1), pentru combinatii complexe este posibil sa omitem cazuri si astfel circuitul nostru se va transforma intr-unul de memorare. Ca sa evitam acest lucru, definim si un caz default.
  • Se poate folosi modulul de test anterior pentru testare, cu observatia ca modulul instantiat se va denumi exemplul3.

Exercitii suplimentare

1. Scrieti un modul de test pentru un sumator pe 4 biti, descris cu ajutorul Verilog astfel:

module sum(
  input[3:0] a,
  input[3:0] b,
  output[4:0] sum,
  );

  assign sum = a + b;

endmodule

2. Scrieti un modul de test pentru un comparator, descris cu ajutorul Verilog astfel:

module comp(
  input[3:0] a,
  input[3:0] b,
  output lt,
  output gt,
  output eq,
  );

  assign lt = a < b;
  assign gt = a > b;
  assign eq = a == b;

endmodule