Diferență între revizuiri ale paginii „Laboratorul 4”

De la WikiLabs
Jump to navigationJump to search
Linia 100: Linia 100:
  
 
=== EXECUTE ===
 
=== EXECUTE ===
 +
 +
Logica combinațională de execuție include unitatea aritmetico-logică (ALU). Aceasta preia operanzii salvați în registrul pipeline R2 și livrează rezultatul în baza codului instrucțiunii din același registru. Pentru instrucțiunile de acces la memoria de date (''LOAD'' și ''STORE''), operandul 1 este trimis spre aceasta ca adresă de date. Instrucțiunea ''STORE'' trimite și operandul 2, cel ce trebuie salvat în memoria de date, și activează semnalul de control de scriere, '''write'''. Instrucțiunea ''LOAD'' preia data citită din memorie, '''data_in''', și o selectează drept rezultat ce trebuie scris în registrul destinație, selecție ce se face comparând codul instrucțiunii din registrul pipeline R2 (codul instrucțiunii aflate în execuție) cu codul instrucțiunii ''LOAD''.
 +
 +
<syntaxhighlight lang="Verilog">
 +
assign write  =  // activ numai cand STORE e in faza de executie // <<<<< COMPLETAȚI CODUL !
 +
assign result = (r2_opcode == 4'b1001) ? data_in : alu_result;
 +
</syntaxhighlight>
 +
 +
Tot logica combinațională din etapa de execuție generează semnalul de control al scrierii în setul de registre. Instrucțiunile NOP și HALT nu trebuie să actualizeze nimic, la fel și instrucțiunea ''STORE''.
 +
 +
<syntaxhighlight lang="Verilog">
 +
always @(*) begin
 +
    case(r2_opcode)
 +
    4'b0000: regs_wen = 0; // NOP
 +
    4'b0001: regs_wen = 1; // ADD
 +
    4'b0010:              // SUB    // <<<<< COMPLETAȚI CODUL !
 +
    4'b1000:              // LOADC  // <<<<< COMPLETAȚI CODUL !
 +
    4'b1001:              // LOAD  // <<<<< COMPLETAȚI CODUL !
 +
    4'b1010:              // STORE  // <<<<< COMPLETAȚI CODUL !
 +
    4'b1111:              // HALT  // <<<<< COMPLETAȚI CODUL !
 +
    // urmeaza implementarea altor operatii
 +
    default: regs_wen = 0;
 +
    endcase
 +
end
 +
</syntaxhighlight>

Versiunea de la data 7 noiembrie 2019 02:47

Arhitectura Harvard

Arhitectura de calculator Harvard are magistrale distincte pentru accesul la program și la date, permițând astfel citirea unei instrucțiuni în paralel cu citirea sau scrierea datei altei instrucțiuni. Programul și datele se află în memorii separate sau într-o memorie comună dar cu porturi multiple de acces.

Asc lab4 harvard.png

Procesorul pipeline

Procesorul are o structură pipeline cu trei niveluri:

  1. FETCH - citirea instrucțiunii din memoria de program
  2. READ - citirea operanzilor din setul de registre
  3. EXECUTE - execuția operației instrucțiunii/accesul în memoria de date

Asc lab4 procesor.png

În această lucrare de laborator se va implementa un procesor pipeline fără gestiunea dependențelor de date sau de control, ce procesează setul de instrucțiuni din laboratoarele precedente. Implementarea va reutiliza integral blocurile ALU și REGS proiectate în laboratorul 1, și cu mici modificări memoria din laboratorul 2.

Schema detaliată a structurii pipeline pune în evidență toate căile de date și semnalele de control. Schema este desenată astfel încât fluxul de instrucțiuni și de date să fie de la stânga la dreapta, cu excepția căii de scriere în setul de registre și a semnalului special de control halt.

Asc lab4 pipeline.png

PC

Contorul de program incrementează în fiecare ciclu de ceas cu excepția cazului în care procesorul a fost oprit de instrucțiunea HALT.

always @(posedge clk) begin
    if(rst)
        pc <= 0;
    else if(halt)
        pc <= pc;
    else 
        pc <= pc + 1;
end

FETCH

Setul de instrucțiuni fiind foarte simplu pentru acest laborator, nu există logică suplimentară în etapa de citire a instrucțiunii. Procesorul trimite spre memoria de program adresa instrucțiunii, adică valoarea PC, și preia instrucțiunea de la ieșirea acesteia.

Registrul pipeline R1

Acesta salvează la finalul ciclului de ceas (de citire a instrucțiunii) instrucțiunea primită de la memoria de program. Actualizarea registrului pipeline este oprită dacă procesorul execută instrucțiunea HALT. La resetare registrul pipeline este încărcat cu 0, adică cu codul instrucțiunii NOP.

always @(posedge clk) begin
    if(rst)
        r1 <= 0;
    else if(halt)
        r1 <= r1;
    else
        r1 <= instr;
end

READ

În etapa de citire a operanzilor procesorul accesează setul de registre pe baza câmpurilor sursă ale instrucțiunii din registrul R1, preia datele din registrele sursă și livrează operanzii pentru a fi stocați în registrul pipeline următor, R2. Pentru instrucțiunea LOADC operandul 2 este preluat direct din câmpul constantă al instrucțiunii din R1. Logica de selecție a multiplexorului operandului 2 se bazează pe comparația codului instrucțiunii curente, r1_opcode, cu codul instrucțiunii LOADC.

assign operand2 = (r1_opcode == 4'b1000) ? r1_instr_data : rdata2; // LOADC are în binar codul 1000

Pentru claritatea codului, folosiți nume dedicate pentru fiecare câmp al instrucțiunii din R1

assign r1_opcode     = r1[15:12];
assign r1_dest       = r1[11: 8];
assign r1_sursa1     = r1[ 7: 4];
assign r1_sursa2     =              // <<<<< COMPLETAȚI CODUL !
assign r1_instr_data =              // <<<<< COMPLETAȚI CODUL !

Registrul pipeline R2

Acest registru pipeline salvează operanzii și o parte a instrucțiunii ce a trecut de etapa de citire, câmpurile sursă și constantă nemaifiind necesare. Spre deosebire de registrul R1 care era declarat ca o singură variabilă r1, registrul R2 este compus din patru variabile distincte. Este foarte important ca după execuția instrucțiunii HALT acest registru pipeline să nu se mai actualizeze - astfel instrucțiunea HALT rămâne în registrul pipeline R2 asigurând blocarea procesorului până la resetare. La resetare registrul pipeline este încărcat cu 0, adică cu codul instrucțiunii NOP.

always @(posedge clk) begin
    if(rst) begin
        r2_opcode     <= 0;
        r2_dest       <= 0;
        r2_operand1   <= 0;
        r2_operand2   <= 0;
    end
    else if(halt) begin
        r2_opcode     <=              // <<<<< COMPLETAȚI CODUL !
         . . . . .
    end
    else begin
        r2_opcode     <= r1_opcode;
        r2_dest       <=              // <<<<< COMPLETAȚI CODUL !
        r2_operand1   <= operand1;
        r2_operand2   <=              // <<<<< COMPLETAȚI CODUL !
    end
end

EXECUTE

Logica combinațională de execuție include unitatea aritmetico-logică (ALU). Aceasta preia operanzii salvați în registrul pipeline R2 și livrează rezultatul în baza codului instrucțiunii din același registru. Pentru instrucțiunile de acces la memoria de date (LOAD și STORE), operandul 1 este trimis spre aceasta ca adresă de date. Instrucțiunea STORE trimite și operandul 2, cel ce trebuie salvat în memoria de date, și activează semnalul de control de scriere, write. Instrucțiunea LOAD preia data citită din memorie, data_in, și o selectează drept rezultat ce trebuie scris în registrul destinație, selecție ce se face comparând codul instrucțiunii din registrul pipeline R2 (codul instrucțiunii aflate în execuție) cu codul instrucțiunii LOAD.

assign write  =  // activ numai cand STORE e in faza de executie // <<<<< COMPLETAȚI CODUL !
assign result = (r2_opcode == 4'b1001) ? data_in : alu_result;

Tot logica combinațională din etapa de execuție generează semnalul de control al scrierii în setul de registre. Instrucțiunile NOP și HALT nu trebuie să actualizeze nimic, la fel și instrucțiunea STORE.

always @(*) begin
    case(r2_opcode)
    4'b0000: regs_wen = 0; // NOP
    4'b0001: regs_wen = 1; // ADD
    4'b0010:               // SUB    // <<<<< COMPLETAȚI CODUL !
    4'b1000:               // LOADC  // <<<<< COMPLETAȚI CODUL !
    4'b1001:               // LOAD   // <<<<< COMPLETAȚI CODUL !
    4'b1010:               // STORE  // <<<<< COMPLETAȚI CODUL !
    4'b1111:               // HALT   // <<<<< COMPLETAȚI CODUL !
    // urmeaza implementarea altor operatii
    default: regs_wen = 0;
    endcase
end