CID aplicatii 4 : Memorii ROM

De la WikiLabs
Versiunea din 20 martie 2022 18:01, autor: Mihai.antonescu (Discuție | contribuții) (Exerciții:)

Memoria ROM

Memoria este un dispozitiv electronic utilizat pentru stocarea datelor. Aceasta poate fi de mai multe feluri, având diverse caracteristici, cum ar fi capacitatea de a-și păstra sau nu conținutul după întreruperea alimentării sau capacitatea de a-și modifica sau nu conținutul după terminarea procesului de producție.

Memoria ROM (Read-Only Memory) este un circuit combinational folosit pentru stocarea unor date ce pot fi accesate cu ajutorul unei intrari de adresă. Asa cum sugerează și numele, memoria ROM clasică nu poate fi modificată. În plus, datorită modului în care este implementată, ea își păstrează conținutul după întreruperea alimentării (este nevolatilă).

Pentru a înțelege termenul de memorie, putem face următorul exercițiu de imaginație: să ne gândim la un dulap cu mai multe sertare. Conținutul memoriei este reprezentat de conținutul sertarelor, iar adresele sunt reprezentate de etichetele lipite pe aceste sertare pentru a le identifica. Dacă avem nevoie de conținutul unui sertar, va trebui să știm eticheta aferentă acestuia (adresa).

O memorie ROM are următorii parametri: numărul de biți ai adresei, care este legat de numarul de locații de memorie (cu n biți de adresă putem forma 2𝑛 combinații diferite, deci putem accesa maxim 2𝑛 locații de memorie) și dimensiunea locației de memorie, care ne spune cât de multă informație poate stoca o locație de memorie.


Să urmărim exemplul din figura următoare:

ROM memory.PNG


Aici, adresa are dimensiunea de 4 biți, asadar putem accesa cu aceasta 16 locații diferite de memorie. Dimensiunea fiecărei locații este de 8 biți, așadar fiecare locație de memorie poate stoca numere de 8 biți. Spunem că memoria ROM este o memorie 16x8. Capacitatea acestei memorii este de 16 x 8 biți = 128 biți.

Exemple

Exemplul 1: Decodorul

Decodorul este circuitul care, pentru o anumită valoare a intrării, va genera la ieșire un șir binar care conține 1 pe poziția cu indicele egal cu valorea intrării și 0 in rest. De exemplu, decodorul de mai jos pe 2 biți va avea următoarea corespondență intrare-iesire:

in out
2'b00 4'b0001
2'b01 4'b0010
2'b10 4'b0100
2'b11 4'b1000

Decoder.PNG

Vom implementa acest circuit ca memorie ROM.

Implementarea Verilog a circuitului

module Decoder(
  input [1:0] in,
  output reg [3:0] out
  );

  always@(in) begin
    case(in)
      2'b00: out = 4'b0001;
      2'b01: out = 4'b0010;
      2'b10: out = 4'b0100;
      2'b11: out = 4'b1000;
      default: out = 4'b0000;
    endcase
  end

endmodule


Implementarea Verilog a modulului de test

`timescale 1ns/1ps

module Decoder_TB();
  reg [1:0] in_tb;
  wire [3:0] out_tb;
  
  integer idx;

  initial begin
    for(idx=0; idx<4; idx = idx + 1) begin
      in_t = idx;
      #1;
    end 
    #2 $stop();
  end

  Decoder DUT(
    .in(in_tb),
    .out(out_tb)
  );

endmodule

Observații:

  • Circuitul Decoder va avea o intrare pe 2 biți, care va reprezenta adresa memoriei ROM și o iesire pe 4 biți, reprezentând conținuturile memoriei ROM corespunzătoare fiecărei combinații de la intrare.
  • Chiar dacă în interiorul instrucțiunii case se acoperă toate cazurile posibile (toate combinațiile de 2 biți), este bine sa punem și un default pentru a evita inserarea în locul circuitului combinațional dorit (aici, memoria ROM) a unui latch.


Exemplul 2: Transcodorul

Trancodorul este o memorie ROM care asociază unei anumite intrări, reprezentată de adresă, un anumit cod, reprezentat de conținutului locației de memorie asociată acelei adrese.

In acest exemplu, vom folosi transcodorul pentru a realiza un modul ce controlează afișajul cu 7 segmente. Acest dispozitiv de afișare este format, așa cum sugerează și numele, din 7 segmente ce pot fi controlate separat. Putem aprinde sau stinge fiecare segment controlând tensiunea aplicată pe acesta. Pentru acest afișaj, comanda este negativă: aplicând 0 logic pe un segment acesta se va aprinde, iar aplicând 1 logic, acesta se va stinge.

Structura unui afisaj cu 7 segmente este prezentată în imaginea de mai jos:

7LED display.PNG

Pentru a realiza controlul afișării, avem nevoie de o memorie ROM ce trebuie să asocieze codului binar al fiecărui element ce poate fi afișat un șir de biți care sting sau aprind diversele segmente ale afișajului, astfel încât să apară imaginea elementului dorit.

Pentru a reprezenta, de exemplu, cele 16 numere hexa, segmentele trebuie aprinse conform Anexei 1. De exemplu, pentru primele 8 numere (0 până la 7), tabelul de asociere este următorul:

Număr Cod intrare Șir de afisare
0 4'b0000 7'b100_0000
1 4'b0001 7'b111_1001
2 4'b0010 7'b010_0100
3 4'b0011 7'b011_0000
4 4'b0100 7'b001_1001
5 4'b0101 7'b001_0010
6 4'b0110 7'b000_0010
7 4'b0111 7'b111_1000

Implementarea Verilog a modulului Display7Seg

module Display7Seg(
  input [3:0] in,
  output reg [6:0] out
  );

  always@(in) begin
    case(in)
      4'd0: out = 7'b1000000;
      4'd1: out = 7'b1111001;
      4'd2: out = 7'b0100100;
      4'd3: out = 7'b0110000;
      4'd4: out = 7'b0011001;
      4'd5: out = 7'b0010010;
      4'd6: out = 7'b0000010;
      4'd7: out = 7'b1111000;
      default: out = 7'b0000110;
    endcase 
  end

endmodule

Exerciții:

Exercițiul 1

Completați codul din Exemplul 2 astfel încât să se poată reprezenta toate numerele în bază hexa, conform Anexei 1

Exercițiul 2

Un procesor are nevoie de un șir de biți numit instrucțiune pentru a știi ce trebuie să facă. Această instrucțiune codează în șirul său de biți toate informațiile necesare.

De exemplu, pentru un grup de instrucțiuni care operează cu registre, aceste informații pot fi:

  • codul instrucțiunii (operation code - opcode): un șir de biți care ne spune ce operație se execută.
  • destinația: un șir de biți care codează numărul registrului destinație.
  • sursa1: un șir de biți care codează numărul registrului folosit ca operand 1.
  • sursa2: un șir de biți care codează numărul registrului folosit ca operand 2.


Vom considera codul operației pe 4 biți, ceea ce inseamnă ca putem avea 16 instrucțiuni diferite. ]n plus, câmpurile care codează destinația și cei doi operanzi vor avea fiecare câte 4 biți, ceea ce inseamnă că vom avea 16 registre cu care putem lucra (R0 ... R15). Adunând dimensiunile fiecărui câmp, vom obține o instrucțiune pe 16 biți.

Instrucțiunile și codul operațiilor:

Vom considera ca procesorul nostru elementar poate suporta următoarele instrucțiuni:

Operație Cod operație Sintaxă Descriere
ADD 4'b0000 ADD Rd Rs1 Rs2 Rd = Rs1 + Rs2
SUB 4'b0001 SUB Rd Rs1 Rs2 Rd = Rs1 - Rs2
OR 4'b0010 OR Rd Rs1 Rs2 Rd = Rs1 OR Rs2
AND 4'b0011 AND Rd Rs1 Rs2 Rd = Rs1 AND Rs2
LOADC 4'b0100 LOADC Rd Rd = constanta

Pentru instrucțiunile prezentate mai sus, vom avea următoarele moduri de codare (de împărțire a informației în cadrul instrucțiunii):

Codare instructiune.png

Registrele:

Cele 16 registre vor fi codate cu un număr egal cu indexul său. De exemplu, R0 va avea codul 4'b000, R1 va avea codul 4'b0001 și așa mai departe.


Considerând cele spuse mai sus, vom avea următoarele exemple de instrucțiuni:

Exemple codare instructiuni.png


Având toate informațiile de mai sus, descrieți o memorie ROM cu adresă pe 4 biți și locații pe 16 biți, care să conțină următorul program, începând cu adresa 0:

LOADC R0, 2h
LOADC R1, 5h
LOADC R2, 7h
ADD R3, R1, R0
SUB R4, R2, R1
OR R5, R3, R4

Exercițiul 3

Proiectati un codor Cezar cu cheie fixa sub forma de memorie rom.

Un codor cezar ia un mesaj si codeaza independent fiecare litera a acestuia pentru a obtine un mesaj cifrat. El se foloseste de o cheie fixa care se aduna la fiecare litera a mesajului. De exemplu pentru cheia "+3": a->d; b->e; c->f;...; y->b;z->c. Observati ca atunci cand alfabetul a ajuns la capat se reia de la inceput. Pentru decodare procedura este fix inversa.

Observatii, sfaturi, task-uri suplimentare:

  • Codorul va putea prelucra doar litere mici si spatiu gol (acesta ramane neschimbat).
  • Cel mai usor mod de a scrie aceasta functionalitate e folosind un bloc de tip "case".
  • Pentru testare, se pot folosii in tb variabile de tip "reg" pe 8b, astfel:
reg [7:0] litera;
litera = "A";
  • Avansat: exista si tipul de date string in SystemVerilog ceea ce permite parcurgerea cu un for a mesajului. In Verilog clasic, astfel de parcurgeri sunt posibile dar sintaxa este mai complicata.
  • Se va folosii codul ASCI pentru valori. Pentru o usoara vizualizare a datelor in simulare, se poate schimba radix-ul in ASCI.
  • Avansat: Pentru a testa automat codul, se poate instantia inca un modul in tb cu aceasi functionalitate pe post de "golden model". Acest modul nu trebuie sa fie sintetizabil si atunci sintaxa permite mai multa libertate (cum ar fi operatia de modulo "%" care este utila aici pentru testare dar provoaca un circuit complicat daca ar fi sintetizata). Iesirile celor 2 module din tb sunt apoi comparate pentru a se verifica ca amblee implementari ofera acelasi rezultat.
  • Implementati si decodorul de tip cezar si testati codorul impreuna cu acesta. Aveti grija ca cele 2 sa aibe aceeasi cheie.

Anexa1

Anexa1.PNG