Упрощенное ядро микропроцессора AVR для платы Марсоход
//Reduced AVRCore, Verilog HDL //Сильно упрощенная версия ядра AVR микропроцессора для Altera CPLD MAX2
module rAVR( input wire reset, input wire clk,
//UFM (User Flash Memory) interface //??нтерфейс к флеш памяти UFM, которая находится внутри чипа MAX2 //Флеш память - последовательная, и адреса и данные передаются сдвиговыми регистрами
//data //сигналы для чтения последовательных данных из UFM input wire drdin, output wire drshift,
//interface to PIO ports //Устройства, которыми может управлять микроконтроллер - это три порта output reg [7:0]port0,//typically connect to LEDs //обычно, на плате Марсоход, подключаем сюда 8 светодиодов output reg [7:0]port1,//typically attached to motors //обычно, на плате Марсоход, подлючаем сюда шаговые двигатели input wire [7:0]port2 //connect to Buttons and other inputs //обычно, на плате Марсоход, подключаем сюда входы четырех кнопочек );
//opcode read from UFM //код инструкции процессора, считанной из флеш UFM reg[15:0]opcode;
//instruction pointer //указатель на текущую комманду процессора в флеш памяти reg[8:0]ip;
//four common purpose registers //четыре регистра общего назначения reg[7:0]register[3:0];
//ALU operands //регистры, куда выбираются операнды для последующего исполнения reg[7:0]alu_operand0; reg[7:0]alu_operand1; reg[2:0]alu_cmd;
reg[2:0]sel_cmd; reg[7:0]alu_result;
//флаги для условных переходов reg flag_z; reg flag_c;
reg flag_z_fixed; reg flag_c_fixed;
//операнд "число" может быть закодирован в коде команды reg sel_imm; wire[7:0]immediate;assign immediate ={ opcode[11:8],opcode[3:0]};
//get operand from register pool according to opcode source register index //выбор операнда источника wire[2:0]src_reg_idx;assign src_reg_idx = opcode[2:0]; reg[7:0]source_val; always @* begin case(src_reg_idx) 0: source_val = register[0]; 1: source_val = register[1]; 2: source_val = register[2]; 3: source_val = register[3]; 4: source_val = port0; 5: source_val = port1; 6: source_val = port2; 7: source_val = port2; endcase end
//get operand from register pool according to opcode destination register index //выбор операнда приемника wire[2:0]dest_reg_idx;assign dest_reg_idx = opcode[6:4]; reg[7:0]dest_val; always @* begin case(dest_reg_idx) 0: dest_val = register[0]; 1: dest_val = register[1]; 2: dest_val = register[2]; 3: dest_val = register[3]; 4: dest_val = port0; 5: dest_val = port1; 6: dest_val = port2; 7: dest_val = port2; endcase end
//назначение основных команд ALU (Арифметико-Логического Устройства) parameter CMD_LSR =3'b000; parameter CMD_CP =3'b001; parameter CMD_SUB =3'b010; parameter CMD_ADD =3'b011; parameter CMD_AND =3'b100; parameter CMD_EOR =3'b101; parameter CMD_OR =3'b110; parameter CMD_MOV =3'b111;
//decode fetched operation //select operands and unify commands (i.e. CP and CPI are similar, SUB and SUBI are similar etc..) //расшифровка кода операции и трансляция похожих команд к единому кодированию //Например: команда SUBI чем-то похожа на SUB //Внимание! Производится только ЧАСТ??ЧНОЕ/НЕПОЛНОЕ декодирование! //Допускается использование только выбраных команд! always @* begin if( (opcode[15:14]==2'b00) && (opcode[13:12]!=2'b11) ) begin sel_imm =1'b0; sel_cmd ={opcode[13],opcode[11:10]}; end else if(opcode[15:14]==2'b10) begin sel_imm =1'b0; sel_cmd =3'b000; end else begin sel_imm =1'b1; case(opcode[15:12]) 4'b0101: sel_cmd = CMD_SUB; 4'b0111: sel_cmd = CMD_AND; 4'b0110: sel_cmd = CMD_OR; 4'b0011: sel_cmd = CMD_CP; 4'b1110: sel_cmd = CMD_MOV; default: sel_cmd = CMD_CP; endcase end end
//fix decoded command //зафиксировать операнды для исполнения always @(posedge clk or posedge reset) begin if(reset) begin alu_operand0 <=0; alu_operand1 <=0; alu_cmd <=0; end else begin if(opcode_ready) begin alu_operand0 <= dest_val;
//ALU //Арифметико-Логическое Устройство always @* begin case(alu_cmd) CMD_CP, CMD_SUB: begin { flag_c, alu_result } = alu_operand0 - alu_operand1; flag_z = ~(|alu_result); end CMD_AND: begin alu_result = alu_operand0 & alu_operand1; flag_z = ~(|alu_result); flag_c = flag_c_fixed;//flag C not changed end CMD_OR: begin alu_result = alu_operand0 | alu_operand1; flag_z = ~(|alu_result); flag_c = flag_c_fixed;//flag C not changed end CMD_MOV: begin alu_result = alu_operand1; flag_c = flag_c_fixed; flag_z = flag_z_fixed; end CMD_ADD: begin { flag_c, alu_result } = alu_operand0 + alu_operand1; flag_z = ~(|alu_result); end CMD_EOR: begin alu_result = alu_operand0 ^ alu_operand1; flag_c = flag_c_fixed;//flag C not changed flag_z = ~(|alu_result); end CMD_LSR: begin alu_result = alu_operand0 >> 1; flag_c = alu_operand0[0]; flag_z = ~(|alu_result); end endcase end
//присвоение результата вычисления ALU в регистр-приемник always @(posedge clk or posedge reset) begin if(reset) begin //reset registers and flags flag_z_fixed <=1'b0; flag_c_fixed <=1'b0;
port0 <=0; port1 <=0; end else begin //fix result only if command not a CP and not BRANCH //результат присваивается только если команда не CP и не переход if( fix_result & (alu_cmd != CMD_CP) & (opcode[15:12]!=4'b1111) ) begin case(dest_reg_idx) 0: register[0]<= alu_result; 1: register[1]<= alu_result; 2: register[2]<= alu_result; 3: register[3]<= alu_result; 4: port0 <= alu_result; 5: port1 <= alu_result; endcase end
//fix alu flags always except BRANCH //флаги исполнения команды фиксируем всегда, кроме переходов if( fix_result & (opcode[15:12]!=4'b1111) ) begin flag_z_fixed <= flag_z; flag_c_fixed <= flag_c; end end end
//вычисление необходимости условного перехода wire[1:0]branch_id; assign branch_id ={opcode[10],opcode[0]};
//управление указателем на код команды во флешке UFM always @(posedge clk or posedge reset) begin if(reset) begin ip <=0; end else begin if(arclkshift) begin //последовательно выдвигаем адрес ip <={ip[7:0],ip[8]}; end else if(addr_inc) begin //go next instruction //переход к следующей инструкции ip <= ip +1'b1; end else begin //fix instruction pointer //jump on condition //изменение указатели при условном переходе if( need_jump & opcode_ready ) ip <= ip +{opcode[9],opcode[9],opcode[9:3]}; end end end
//UFM (User Flash Memory) reading process //управление встроенной флеш памятью, где у нас хранится программа reg[5:0]ufm_counter;
//count UFM read states //считаем такты при чтении из флешки UFM always @(posedge clk or posedge reset) begin if(reset) ufm_counter <=0; else begin if(opcode_ready) begin if(need_jump) ufm_counter <=0; else ufm_counter <=10; end else ufm_counter <= ufm_counter +1'b1; end end
//Shift IN data from serial flash of UFM //последовательно принимаем код инструкции от флешки UFM always @(posedge clk or posedge reset) begin if(reset) opcode <=0; else begin if(drshift) opcode <={opcode[14:0],drdin}; end end
Подробнее...