§ Код ядра

Описание пинов:
  • clock — тактовая частота, срабатывает на позитивном и негативном фронте, который записывает результаты в регистры
  • reset_n — при 0, сброс процессора
  • pc — указатель Program Counter
  • ir — данные 16 битные из rom[pc]
  • address — указатель в оперативную память, до 64К
  • data_i — входящие в процессор данные
  • data_o — данные на запись
  • intr — прерывание, срабатывает на перемене состояния
  • vect — номер прерывания от 0 до 7
  • we — сигнал записи, длительность сигнала — 1 такт на 1 инструкцию
  • read — сигнал, что данные были именно прочитаны из памяти (тоже 1 такт на 1 инструкцию)
Ревизия обновлена: 14 янв 2024.

§ Код ядра

Ниже представлен готовый код ядра процессора.
/* verilator lint_off WIDTH */
/* verilator lint_off CASEX */
/* verilator lint_off CASEINCOMPLETE */
/* verilator lint_off UNOPTFLAT */
module avr
(
    input               clock,
    input               reset_n,
    // Программная память
    output reg  [15:0]  pc,         // Программный счетчик
    input       [15:0]  ir,         // Инструкция из памяти
    // Оперативная память
    output reg  [15:0]  address,    // Указатель на память RAM (sram)
    input       [ 7:0]  data_i,     // = memory[ address ]
    output reg  [ 7:0]  data_o,     // Запись в память по address
    output reg          we,         // =1 Запись в память
    output reg          read,       // =1 Запрос чтения из памяти
    // Внешнее прерывание #0..7
    input               intr,
    input       [ 2:0]  vect
);
initial begin
    address = 1'b0;
    pc      = 1'b0;
    we      = 1'b0;
    data_o  = 1'b0;
    read    = 1'b0;
    // Регистры 0-15
    r[0] = 8'h00; r[4] = 8'h00; r[8]  = 8'h00; r[12] = 8'h01;
    r[1] = 8'h00; r[5] = 8'h00; r[9]  = 8'h00; r[13] = 8'h00;
    r[2] = 8'h00; r[6] = 8'h00; r[10] = 8'h00; r[14] = 8'h00;
    r[3] = 8'h00; r[7] = 8'h00; r[11] = 8'h00; r[15] = 8'h00;
    // Регистры 16-31
    r[16] = 8'h00; r[20] = 8'h00; r[24] = 8'h00; r[28] = 8'h00;
    r[17] = 8'h00; r[21] = 8'h00; r[25] = 8'h00; r[29] = 8'h00;
    r[18] = 8'h00; r[22] = 8'h00; r[26] = 8'h00; r[30] = 8'h05;
    r[19] = 8'h00; r[23] = 8'h00; r[27] = 8'h00; r[31] = 8'hFA;
end
localparam
    SPDEC = 1,
    SPINC = 2;
// ---------------------------------------------------------------------
// Проксирование памяти
// ---------------------------------------------------------------------
reg [7:0] din;
always @* begin
    casex (address)
        // Регистры
        16'b0000_0000_000x_xxxx: din = r[ address[4:0] ];
        // Процессор
        16'h005B: din = rampz;
        16'h005D: din = sp[ 7:0];
        16'h005E: din = sp[15:8];
        16'h005F: din = sreg;
        // Память
        default:  din = data_i;
    endcase
end
// ---------------------------------------------------------------------
// Регистры процессора, в том числе системные
// ---------------------------------------------------------------------
// Регистры
reg [ 7:0]  r[32];                      // Регистры
reg [ 1:0]  tstate  = 0;                // Машинное состояние
reg [15:0]  latch   = 0;                // Записанный опкод
reg [15:0]  pclatch = 0;                // Для LPM / SPM
reg [15:0]  sp      = 16'h1FFF;         // Stack Pointer 8k
reg [ 7:0]  sreg    = 8'b0000_0000;     // Status Register
reg [ 4:0]  alu     = 0;                // Режим работы АЛУ
// Команды на обратном фронте
reg         reg_w   = 0;                // Писать в регистр
reg         sreg_w  = 0;                // Писать в SREG из АЛУ
reg [ 4:0]  reg_id  = 0;                // В какой регистр писать
reg [ 1:0]  sp_mth  = 0;                // Увеличение или уменьшение стека
// 16 битные регистры
reg         reg_ww  = 1'b0;             // Писать в X,Y,Z
reg         reg_ws  = 1'b0;             // =1 Источник АЛУ; =0 Источник регистр `wb2`
reg         reg_wm  = 1'b0;             // Запись в 1:0
reg [ 1:0]  reg_idw = 1'b0;             // Номер 16-битного регистра
reg [15:0]  wb2     = 1'b0;             // Данные для записи в X,Y,Z
reg [ 7:0]  rampz   = 1'b0;             // Верхняя память для E-функции
// Провода
wire [15:0] opcode = tstate ? latch : ir;
wire [15:0] X   = {r[27], r[26]};
wire [15:0] Y   = {r[29], r[28]};
wire [15:0] Z   = {r[31], r[30]};
wire [15:0] Xm  = X - 1'b1;
wire [15:0] Xp  = X + 1'b1;
wire [15:0] Ym  = Y - 1'b1;
wire [15:0] Yp  = Y + 1'b1;
wire [15:0] Zm  = Z - 1'b1;
wire [15:0] Zp  = Z + 1'b1;
wire [ 5:0] q   = {opcode[13], opcode[11:10], opcode[2:0]};
wire [ 4:0] rd  =  opcode[8:4];
wire [ 4:0] rr  = {opcode[9], opcode[3:0]};
wire [ 4:0] rdi = {1'b1, opcode[7:4]};
wire [ 4:0] rri = {1'b1, opcode[3:0]};
wire [ 7:0] K   = {opcode[11:8], opcode[3:0]};
// Управление счетчиком
reg         skip_instr  = 1'b0;
wire [15:0] pcnext      = pc + 1'h1;
wire [15:0] pcnext2     = pc + 2'h2;
wire        is_call     = {opcode[14], opcode[3:1]} == 4'b0111;
// ---------------------------------------------------------------------
// Арифметико-логическое устройство
// ---------------------------------------------------------------------
reg  [ 7:0] op1, op2, alu_res, alu_sreg;
reg  [15:0] op1w, resw;

// Таймер прерываний
reg        intr_trigger = 1'b0;
reg        intr_prev    = 1'b0;
reg [2:0]  intr_vect    = 1'b0;
// ---------------------------------------------------------------------
// Главное исполнительное устройство
// ---------------------------------------------------------------------
always @(posedge clock)
// Сброс процессора
if (reset_n == 1'b0) begin
    pc      <= 1'b0;
    tstate  <= 1'b0;
end
// Рабочее состояние
else begin
    we     <= 1'b0;
    read   <= 1'b0; // Чтение из памяти
    reg_w  <= 1'b0;
    sreg_w <= 1'b0;
    sp_mth <= 1'b0; // Ничего не делать с SP
    reg_ww <= 1'b0; // Ничего не делать с X,Y,Z
    reg_ws <= 1'b0; // Источник регистр wb2
    reg_wm <= 1'b0; // Источник регистр resw
    if (tstate == 0) latch <= ir;
    // Код пропуска инструкции (JMP, CALL, LDS, STS)
    if (skip_instr) begin
        casex (opcode)
            16'b1001_010x_xxxx_11xx, // CALL | JMP
            16'b1001_00xx_xxxx_0000: // LDS  | STS
                pc <= pcnext + 1;
            default:
                pc <= pcnext;
        endcase
        skip_instr <= 0;
    end
    // Вызов прерывания таймера
    // -----------------------------------------------------------------
    else if (intr_trigger) begin
        case (tstate)
            // Запись PCL
            0: begin
                tstate  <= 1;
                address <= sp;
                data_o  <= pc[7:0];
                we      <= 1'b1;
                sp_mth  <= SPDEC;
            end
            // Запись PCH
            1: begin
                tstate  <= 0;
                address <= sp;
                data_o  <= pc[15:8];
                we      <= 1'b1;
                sp_mth  <= SPDEC;
                pc      <= {intr_vect, 1'b0};  // ISR(INTx_vect)
                intr_trigger <= 0;             // Переход к обычному исполнению
                // Сброс флага I->0 (sreg)
                alu     <= 11;
                op2     <= {1'b0, sreg[6:0]};
                sreg_w  <= 1'b1;
            end
        endcase
    end
    // Вызов прерывания IRQ#x срабатывает на переключении intr
    // -----------------------------------------------------------------
    else if (sreg[7] && tstate == 0 && intr ^ intr_prev) begin
        intr_vect    <= vect + 1;   // Вектор 0..7
        intr_prev    <= intr;       // Сохранить предыдущее состояние
        intr_trigger <= 1'b1;       // Вызов прерывания
    end
    // Исполнение опкодов
    // -----------------------------------------------------------------
    else casex (opcode)
        // NOP, BREAK
        16'b0000_0000_0000_0000: pc <= pcnext;
        16'b1001_0101_1001_1000: pc <= pcnext;
        // [1T] LDI Rd, K
        16'b1110_xxxx_xxxx_xxxx: begin
            pc      <= pcnext;
            alu     <= 0;       // Инструкция LDI
            op2     <= K;
            reg_w   <= 1'b1;
            reg_id  <= rdi;
        end
        // [1T] RJMP k | IJMP | EIJMP
        16'b1100_xxxx_xxxx_xxxx: pc <= pcnext + {{4{opcode[11]}}, opcode[11:0]};
        16'b1001_0100_000x_1001: pc <= Z;
        // [2T] JMP k
        16'b1001_010x_xxxx_110x:
        case (tstate)
            0: begin tstate <= 1; pc <= pcnext; end
            1: begin tstate <= 0; pc <= ir; end
        endcase
        // [1T] <ALU> Rd, Rr
        16'b000x_01xx_xxxx_xxxx, // 1 CPC, 5 CP
        16'b000x_1xxx_xxxx_xxxx, // 2 SBC, 3 ADD, 6 SUB, 7 ADC
        16'b0010_0xxx_xxxx_xxxx, // 8 AND, 9 EOR
        16'b0010_10xx_xxxx_xxxx: // A OR
        begin
            pc      <= pcnext;
            alu     <= opcode[13:10];
            op1     <= r[rd];
            op2     <= r[rr];
            reg_id  <= rd;
            // CP, CPС не писать
            reg_w   <= (opcode[13:10] != 4'b0001 && opcode[13:10] != 4'b0101);
            sreg_w  <= 1'b1;
        end
        // [1T] BRB[C/S] k
        16'b1111_0xxx_xxxx_xxxx:
        begin
            if (sreg[ opcode[2:0] ] ^ opcode[10])
                pc <= pcnext + {{9{opcode[9]}}, opcode[9:3]};
            else
                pc <= pcnext;
        end
        // [1T] АЛУ с непосредственным операндом
        16'b0011_xxxx_xxxx_xxxx, // CPI
        16'b0100_xxxx_xxxx_xxxx, // SBCI
        16'b0101_xxxx_xxxx_xxxx, // SUBI
        16'b0110_xxxx_xxxx_xxxx, // ORI
        16'b0111_xxxx_xxxx_xxxx: // ANDI
        begin
            pc      <= pcnext;
            op1     <= r[rdi];
            op2     <= K;
            reg_id  <= rdi;
            reg_w   <= (opcode[15:12] != 4'b0011); // CPI не писать
            sreg_w  <= 1'b1;
            case (opcode[15:12])
                4'b0011: alu <= 5;  // CPI
                4'b0100: alu <= 2;  // SBCI
                4'b0101: alu <= 6;  // SUBI
                4'b0110: alu <= 10; // ORI
                4'b0111: alu <= 8;  // ANDI
            endcase
        end
        // [1T] MOV Rd, Rr
        16'b0010_11xx_xxxx_xxxx:
        begin
            pc      <= pcnext;
            alu     <= 0;
            op2     <= r[rr];
            reg_id  <= rd;
            reg_w   <= 1'b1;
        end
        // [2T] RCALL k | ICALL | EICALL | CALL
        16'b1101_xxxx_xxxx_xxxx,
        16'b1001_0101_000x_1001,
        16'b1001_010x_xxxx_111x:
        case (tstate)
            // Запись PCL
            0: begin
                tstate  <= 1;
                address <= sp;
                data_o  <= is_call ? pcnext2[7:0] : pcnext[7:0];
                we      <= 1'b1;
                sp_mth  <= SPDEC;
                pc      <= pcnext;
            end
            // Запись PCH
            1: begin
                tstate  <= 0;
                address <= sp;
                data_o  <= is_call ? pcnext[15:8] : pc[15:8];
                we      <= 1'b1;
                sp_mth  <= SPDEC;
                // Метод вызова
                /* CALL */       if (is_call)    pc <= ir;
                /* RCALL */ else if (opcode[14]) pc <= (pc + {{4{opcode[11]}}, opcode[11:0]});
                /* ICALL */ else                 pc <= Z;
            end
        endcase
        // [3T] 9508 RET | 9518 RETI
        16'b1001_0101_000x_1000:
        case (tstate)
            // Указатель адреса
            0: begin
                tstate   <= 1;
                read     <= 1;
                address  <= sp + 1;
                sp_mth   <= SPINC;
            end
            // Чтение PCH
            1: begin
                tstate   <= 2;
                read     <= 1;
                pc[15:8] <= din;
                address  <= sp + 1;
                sp_mth   <= SPINC;
            end
            // Чтение PCL
            2: begin
                tstate   <= 0;
                pc[ 7:0] <= din;
                alu      <= 11;
                op2      <= {sreg[7] | opcode[4], sreg[6:0]};
                sreg_w   <= 1;
            end
        endcase
        // [2T] LD Rd, (X|Y|Z)+
        // [1T] ST Rd, (X|Y|Z)+
        16'b1001_00xx_xxxx_1100, // X
        16'b1001_00xx_xxxx_1101, // X+
        16'b1001_00xx_xxxx_1110, // -X
        16'b1001_00xx_xxxx_1001, // Y+
        16'b1001_00xx_xxxx_1010, // -Y
        16'b1001_00xx_xxxx_0001, // Z+
        16'b1001_00xx_xxxx_0010: // -Z
        case (tstate)
            // Установка указателя на память
            0: begin
                tstate <= opcode[9] ? 0 : 1;
                pc     <= pcnext;
                data_o <= r[rd];
                we     <=  opcode[9];
                read   <= !opcode[9];
                // Выбор адреса
                case (opcode[3:0])
                    4'b11_00: begin address <= X;  end
                    4'b11_01: begin address <= X;  wb2 <= Xp; reg_idw <= 2'b01; reg_ww <= 1; end
                    4'b11_10: begin address <= Xm; wb2 <= Xm; reg_idw <= 2'b01; reg_ww <= 1; end
                    4'b10_01: begin address <= Y;  wb2 <= Yp; reg_idw <= 2'b10; reg_ww <= 1; end
                    4'b10_10: begin address <= Ym; wb2 <= Ym; reg_idw <= 2'b10; reg_ww <= 1; end
                    4'b00_01: begin address <= Z;  wb2 <= Zp; reg_idw <= 2'b11; reg_ww <= 1; end
                    4'b00_10: begin address <= Zm; wb2 <= Zm; reg_idw <= 2'b11; reg_ww <= 1; end
                endcase
            end
            // Запись в регистр Rd (LD)
            1: begin
                tstate  <= 0;
                alu     <= 0;
                op2     <= din;
                reg_w   <= 1;
                reg_id  <= rd;
            end
        endcase
        // [2T] LDD Y+q, Z+q
        // [1T] STD Y+q, Z+q
        16'b10x0_xxxx_xxxx_xxxx: // Y,Z
        case (tstate)
            // Установка указателя на память
            0: begin
                tstate  <= opcode[9] ? 0 : 1;
                pc      <= pcnext;
                address <= (opcode[3] ? Y : Z) + q;
                data_o  <= r[rd];
                we      <=  opcode[9];
                read    <= !opcode[9];
            end
            // Запись в регистр Rd
            1: begin
                tstate  <= 0;
                alu     <= 0;
                op2     <= din;
                reg_w   <= 1;
                reg_id  <= rd;
            end
        endcase
        // [1T] 0=COM, 1=NEG, 2=SWAP, 3=INC, 5=ASR, 6=LSR, 7=ROR, 10=DEC
        16'b1001_010x_xxxx_00xx,
        16'b1001_010x_xxxx_011x,
        16'b1001_010x_xxxx_0101,
        16'b1001_010x_xxxx_1010: begin
            pc      <= pcnext;
            op1     <= r[rd];   // Rd
            reg_w   <= 1;
            sreg_w  <= 1;
            reg_id  <= rd;
            case (opcode[3:0])
                4'b0000: alu <= 5'h0C; // COM
                4'b0001: alu <= 5'h0D; // NEG
                4'b0010: alu <= 5'h0E; // SWAP
                4'b0011: alu <= 5'h0F; // INC
                4'b0101: alu <= 5'h10; // ASR
                4'b0110: alu <= 5'h11; // LSR
                4'b0111: alu <= 5'h12; // ROR
                4'b1010: alu <= 5'h13; // DEC
            endcase
        end
        // [2T] MOVW Rd, Rr
        16'b0000_0001_xxxx_xxxx:
        case (tstate)
            // LO регистр
            0: begin
                tstate  <= 1;
                pc      <= pcnext;
                alu     <= 0;
                op2     <= r[ {opcode[3:0], 1'b0} ];
                reg_id  <=    {opcode[7:4], 1'b0};
                reg_w   <= 1;
            end
            // HI регистр
            1: begin
                tstate  <= 0;
                alu     <= 0;
                op2     <= r[ {opcode[3:0], 1'b1} ];
                reg_id  <=    {opcode[7:4], 1'b1};
                reg_w   <= 1;
            end
        endcase
        // [1T] <ADIW|SBIW> Rd, K
        16'b1001_0110_xxxx_xxxx, // ADIW
        16'b1001_0111_xxxx_xxxx: // SBIW
        begin
            pc      <= pcnext;
            alu     <= opcode[8] ? 5'h15 : 5'h14;
            case (opcode[5:4])
                0: op1w <= {r[25], r[24]};
                1: op1w <= {r[27], r[26]};
                2: op1w <= {r[29], r[28]};
                3: op1w <= {r[31], r[30]};
            endcase
            op2     <= {opcode[7:6], opcode[3:0]};
            reg_idw <= opcode[5:4];
            reg_ww  <= 1;
            reg_ws  <= 1;
            sreg_w  <= 1;
        end
        // [2T] LPM Rd, Z
        16'b1001_0101_110x_1000, // LPM R0, Z  | ELPM R0, Z
        16'b1001_000x_xxxx_01x0, // LPM Rd, Z  | ELPM Rd, Z
        16'b1001_000x_xxxx_01x1: // LPM Rd, Z+ | ELPM Rd, Z+
        case (tstate)
            // Считывание байта
            0: begin
                tstate  <= 1;
                pclatch <= pcnext;
                // Если ELPM, то записать в старший бит rampz[0]
                if (opcode[1] || (opcode[10] && opcode[4]))
                     pc <= {rampz[0], Z[15:1]}; // ELPM
                else pc <= Z[15:1];             // LPM
            end
            // Запись
            1: begin
                tstate  <= 0;
                pc      <= pclatch;
                alu     <= 0;       // LDI
                reg_idw <= 2'b11;   // Z
                reg_ww  <= (!opcode[10] & opcode[0]); // Z+
                wb2     <= Zp;
                reg_w   <= 1;
                reg_id  <= opcode[10] ? 0 : rd;         // R0, Rd
                op2     <= Z[0] ? ir[15:8] : ir[7:0];   // Hi, Lo
            end
        endcase
        // [2T] IN  Rd, A
        // [1T] OUT A, Rd
        16'b1011_xxxx_xxxx_xxxx:
        case (tstate)
            // Установка адреса
            0: begin
                tstate  <= opcode[11] ? 0 : 1;
                pc      <= pcnext;
                data_o  <= r[rd];
                we      <=  opcode[11];
                read    <= !opcode[11];
                address <= {opcode[10:9], opcode[3:0]} + 16'h20;
            end
            // Запись регистра
            1: begin
                tstate  <= 0;
                alu     <= 0;
                op2     <= din;
                reg_id  <= rd;
                reg_w   <= 1;
            end
        endcase
        // [1T] SBR[C,S] Rd, b
        // [1T] SBRS Rd, b
        16'b1111_11xx_xxxx_0xxx: begin
            pc <= pcnext;
            if (r[rd][ opcode[2:0] ] == opcode[9])
                skip_instr <= 1;
        end
        // [2T] SBI[C,S] A, b
        // [2T] SBIS A, b
        16'b1001_10x1_xxxx_xxxx: // C=0,S=1
        casex (tstate)
            // Запрос чтения
            0: begin
                tstate  <= 1;
                read    <= 1;
                address <= opcode[7:3] + 16'h20;
            end
            // Вычисление бита
            1: begin
                tstate  <= 0;
                pc      <= pcnext;
                if (din[ opcode[2:0] ] == opcode[9])
                    skip_instr <= 1;
            end
        endcase
        // [1T] CPSE Rd, Rr
        16'b0001_00xx_xxxx_xxxx: begin
            if (r[rd] == r[rr]) skip_instr <= 1;
            pc <= pcnext;
        end
        // [3T] LDS Rd, Mem
        // [2T] STS Mem, Rd
        16'b1001_00xx_xxxx_0000: // 0=lds, 1=sts
        case (tstate)
            // К следующему коду
            0: begin
                tstate  <= 1;
                pc      <= pcnext;
            end
            // Запись в память или выбор регистра
            1: begin
                tstate  <= opcode[9] ? 0 : 2; // 1=STS, 0=LDS
                pc      <= pcnext;
                reg_id  <= rd;
                address <= ir;
                data_o  <= r[rd];
                we      <=  opcode[9];
                read    <= !opcode[9];
            end
            // Запись в регистр
            2: begin
                tstate <= 0;
                alu    <= 0;
                reg_w  <= 1;
                op2    <= din;
            end
        endcase
        // [1T] BCLR, BSET
        16'b1001_0100_xxxx_1000: begin
            case (opcode[6:4])
                0: op2 <= {sreg[7:1], !opcode[7]};
                1: op2 <= {sreg[7:2], !opcode[7], sreg[0]};
                2: op2 <= {sreg[7:3], !opcode[7], sreg[1:0]};
                3: op2 <= {sreg[7:4], !opcode[7], sreg[2:0]};
                4: op2 <= {sreg[7:5], !opcode[7], sreg[3:0]};
                5: op2 <= {sreg[7:6], !opcode[7], sreg[4:0]};
                6: op2 <= {sreg[7],   !opcode[7], sreg[5:0]};
                7: op2 <= {!opcode[7],            sreg[6:0]};
            endcase
            alu     <= 11;
            sreg_w  <= 1;
            pc      <= pcnext;
        end
        // [1T] PUSH Rd
        16'b1001_001x_xxxx_1111: begin
            pc      <= pcnext;
            data_o  <= r[rd];
            we      <= 1'b1;
            address <= sp;
            sp_mth  <= SPDEC;
        end
        // [2T] POP Rd
        16'b1001_000x_xxxx_1111:
        case (tstate)
            // Указатель адреса
            0: begin
                tstate  <= 1;
                pc      <= pcnext;
                address <= sp + 1;
                sp_mth  <= SPINC;
                read    <= 1;
            end
            // Запись в регистр
            1: begin
                tstate  <= 0;
                alu     <= 0;
                op2     <= din;
                reg_id  <= rd;
                reg_w   <= 1;
            end
        endcase
        // [1T] BST Rd, b
        16'b1111_101x_xxxx_0xxx: begin
            pc      <= pcnext;
            alu     <= 11;
            op2     <= {sreg[7], r[rd][ opcode[2:0] ], sreg[5:0]};
            sreg_w  <= 1;
        end
        // [1T] BLD Rd, b
        16'b1111_100x_xxxx_0xxx: begin
            pc      <= pcnext;
            alu     <= 22;  // BLD
            op1     <= r[rd];
            op2     <= opcode[2:0];
            reg_id  <= rd;
            reg_w   <= 1;
        end
        // [2T] CBI / SBI A, b
        16'b1001_10x0_xxxx_xxxx:
        case (tstate)
            // Чтение из порта
            0: begin
                tstate  <= 1;
                read    <= 1;
                pc      <= pcnext;
                address <= opcode[7:3] + 16'h20;
            end
            // Запись в порт
            1: begin
                tstate <= 1'b0;
                we     <= 1'b1;
                case (opcode[2:0])
                    0: data_o <= {din[7:1], opcode[9]};
                    1: data_o <= {din[7:2], opcode[9], din[0]};
                    2: data_o <= {din[7:3], opcode[9], din[1:0]};
                    3: data_o <= {din[7:4], opcode[9], din[2:0]};
                    4: data_o <= {din[7:5], opcode[9], din[3:0]};
                    5: data_o <= {din[7:6], opcode[9], din[4:0]};
                    6: data_o <= {din[  7], opcode[9], din[5:0]};
                    7: data_o <= {          opcode[9], din[6:0]};
                endcase
            end
        endcase
        // MUL
        16'b1001_11xx_xxxx_xxxx: begin
            pc      <= pcnext;
            alu     <= 23;
            op1     <= r[rd];
            op2     <= r[rr];
            reg_wm  <= 1;
        end
        // MULS d16..31, r16..31
        16'b0000_0010_xxxx_xxxx: begin
            pc      <= pcnext;
            alu     <= 24;
            op1     <= r[ {1'b1, opcode[7:4]} ];
            op2     <= r[ {1'b1, opcode[3:0]} ];
            reg_wm  <= 1;
        end
        // MULSU d16..23, r16..23
        16'b0000_0011_0xxx_0xxx: begin
            pc      <= pcnext;
            alu     <= 25;
            op1     <= r[ {2'b10, opcode[6:4]} ];
            op2     <= r[ {2'b10, opcode[2:0]} ];
            reg_wm  <= 1;
        end
    endcase
end
// Запись в регистры
always @(negedge clock)
begin
    // Запись в регистр
    if (reg_w) r[ reg_id ] <= alu_res;
    // Запись в SREG
    if (sreg_w) sreg <= alu_sreg;
    // Инкремент или декремент
    case (sp_mth)
        SPDEC: sp <= sp - 1'b1;
        SPINC: sp <= sp + 1'b1;
    endcase
    // Автоинкремент или декремент X,Y,Z
    if (reg_ww) begin
        case (reg_idw)
            0: {r[25], r[24]} <= reg_ws ? resw : wb2; // W
            1: {r[27], r[26]} <= reg_ws ? resw : wb2; // X
            2: {r[29], r[28]} <= reg_ws ? resw : wb2; // Y
            3: {r[31], r[30]} <= reg_ws ? resw : wb2; // Z
        endcase
    end
    // Инструкции MUL
    if (reg_wm) {r[1], r[0]} <= resw;
    // Запись в порты или регистры
    if (we) begin
        case (address)
            // Системные регистры
            16'h005B: rampz         <= data_o; // Верхняя память ROM
            16'h005D: sp[ 7:0]      <= data_o; // SPL
            16'h005E: sp[15:8]      <= data_o; // SPH
            16'h005F: sreg          <= data_o; // SREG
            // Запись в регистры как в память
            default:  if (address < 16'h20) r[ address[4:0] ] <= data_o;
        endcase
    end
end

// Режим работы АЛУ
// ---------------------------------------------------------------------
// 0 LDI    9  EOR      11 LSR      19 MULSU
// 1 CPC    A  OR       12 ROR
// 2 SBC    B  <SREG>   13 DEC
// 3 ADD    C  COM      14 ADIW
// 5 CP     D  NEG      15 SBIW
// 6 SUB    E  SWAP     16 BLD
// 7 ADC    F  INC      17 MUL
// 8 AND    10 ASR      18 MULS
// ---------------------------------------------------------------------
// Вычисления
wire [7:0] sub      = op1 - op2;
wire [7:0] add      = op1 + op2;
wire [8:0] sbc      = op1 - op2 - sreg[0];
wire [7:0] adc      = op1 + op2 + sreg[0];
wire [7:0] lsr      = {1'b0,    op1[7:1]};
wire [7:0] ror      = {sreg[0], op1[7:1]};
wire [7:0] asr      = {op1[7],  op1[7:1]};
wire [7:0] neg      = -op1;
wire [7:0] inc      = op1 + 1;
wire [7:0] dec      = op1 - 1;
wire [7:0] com      = ~op1;
wire [7:0] swap     = {op1[3:0], op1[7:4]};
// 16 битные вычисления
wire [15:0] adiw    = op1w + op2;
wire [15:0] sbiw    = op1w - op2;
wire [15:0] mul     = op1[7:0] * op2[7:0];
wire [15:0] mulsu   = {{8{op1[7]}}, op1[7:0]} * op2[7:0];
wire [15:0] muls    = {{8{op1[7]}}, op1[7:0]} * {{8{op2[7]}}, op2[7:0]};
// Флаги переполнения после сложения и вычитания
wire add_flag_v     = (op1[7] &  op2[7] & !alu_res[7]) | (!op1[7] & !op2[7] & alu_res[7]);
wire sub_flag_v     = (op1[7] & !op2[7] & !alu_res[7]) | (!op1[7] &  op2[7] & alu_res[7]);
wire neg_flag_v     =  alu_res == 8'h80;
// Флаги половинного переполнения после сложения и вычитания
wire add_flag_h     = ( op1[3] & op2[3]) | (op2[3] & !alu_res[3]) | (!alu_res[3] &  op1[3]);
wire sub_flag_h     = (!op1[3] & op2[3]) | (op2[3] &  alu_res[3]) | ( alu_res[3] & !op1[3]);
wire neg_flag_h     =   op1[3] | (op1[3] & alu_res[3]) | alu_res[3];
// Флаги ADIW, SBIW
wire adiw_c =  op1w[15] & !resw[15];
wire adiw_v = !op1w[15] &  resw[15];
// Логические флаги
wire [7:0] set_logic_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ alu_res[7],
    /* v */ 1'b0,
    /* n */ alu_res[7],
    /* z */ alu_res[7:0] == 0,
    /* c */ sreg[0]
};
// Флаги после вычитания с переносом
wire [7:0] set_subcarry_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sub_flag_h,
    /* s */ sub_flag_v ^ alu_res[7],
    /* v */ sub_flag_v,
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0) & sreg[1],
    /* c */ sbc[8]
};
// Флаги после вычитания
wire [7:0] set_subtract_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sub_flag_h,
    /* s */ sub_flag_v ^ alu_res[7],
    /* v */ sub_flag_v,
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ op1 < op2
};
// Флаги после COM
wire [7:0] set_com_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ alu_res[7],
    /* v */ 1'b0,
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ 1'b1
};
// Флаги после NEG
wire [7:0] set_neg_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ neg_flag_h,
    /* s */ neg_flag_v ^ alu_res[7],
    /* v */ neg_flag_v,
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ op1 != 0
};
// Флаги после сложения
wire [7:0] set_add_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ add_flag_h,
    /* s */ add_flag_v ^ alu_res[7],
    /* v */ add_flag_v,
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ op1 + op2 >= 9'h100
};
// Флаги после сложения с переносом
wire [7:0] set_adc_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ add_flag_h,
    /* s */ add_flag_v ^ alu_res[7],
    /* v */ add_flag_v,
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ op1 + op2 + sreg[0] >= 9'h100
};
// Флаги после логической операции сдвига вправо
wire [7:0] set_lsr_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ op1[0],
    /* v */ alu_res[7] ^ op1[0],
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ op1[0]
};
// Флаги после INC
wire [7:0] set_inc_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ (alu_res == 8'h80) ^ alu_res[7],
    /* v */ (alu_res == 8'h80),
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ sreg[0]
};
// Флаги после DEC
wire [7:0] set_dec_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ (alu_res == 8'h7F) ^ alu_res[7],
    /* v */ (alu_res == 8'h7F),
    /* n */ alu_res[7],
    /* z */ (alu_res[7:0] == 0),
    /* c */ sreg[0]
};
// Флаги после ADIW
wire [7:0] set_adiw_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ adiw_v ^ resw[15],
    /* v */ adiw_v,
    /* n */ resw[15],
    /* z */ (resw[15:0] == 0),
    /* c */ adiw_c
};
// Флаги после SBIW
wire [7:0] set_sbiw_flag = {
    /* i */ sreg[7],
    /* t */ sreg[6],
    /* h */ sreg[5],
    /* s */ adiw_c ^ resw[15],
    /* v */ adiw_c,
    /* n */ resw[15],
    /* z */ (resw[15:0] == 0),
    /* c */ adiw_v
};
// Флаги после MUL, MULS, MULSU
wire [7:0] set_mul_flag   = {sreg[7:2], /* z */ (mul  [15:0] == 0), /* c */ mul  [15]};
wire [7:0] set_muls_flag  = {sreg[7:2], /* z */ (muls [15:0] == 0), /* c */ muls [15]};
wire [7:0] set_mulsu_flag = {sreg[7:2], /* z */ (mulsu[15:0] == 0), /* c */ mulsu[15]};

always @(*)
begin

    alu_sreg = sreg;
    case (alu)
        /* LDI   */ 0:  begin alu_res = op2; end
        /* CPC   */ 1:  begin alu_res = sbc[7:0];   alu_sreg = set_subcarry_flag; end
        /* SBC   */ 2:  begin alu_res = sbc[7:0];   alu_sreg = set_subcarry_flag; end
        /* ADD   */ 3:  begin alu_res = add;        alu_sreg = set_add_flag;      end
        /* CP    */ 5:  begin alu_res = sub;        alu_sreg = set_subtract_flag; end
        /* SUB   */ 6:  begin alu_res = sub;        alu_sreg = set_subtract_flag; end
        /* ADC   */ 7:  begin alu_res = adc;        alu_sreg = set_adc_flag;      end
        /* AND   */ 8:  begin alu_res = op1 & op2;  alu_sreg = set_logic_flag; end
        /* EOR   */ 9:  begin alu_res = op1 ^ op2;  alu_sreg = set_logic_flag; end
        /* OR    */ 10: begin alu_res = op1 | op2;  alu_sreg = set_logic_flag; end
        /* SREG  */ 11: begin                       alu_sreg = op2; end
        /* COM   */ 12: begin alu_res = com;        alu_sreg = set_com_flag; end
        /* NEG   */ 13: begin alu_res = neg;        alu_sreg = set_neg_flag; end
        /* SWAP  */ 14: begin alu_res = swap;       end
        /* INC   */ 15: begin alu_res = inc;        alu_sreg = set_inc_flag; end
        /* ASR   */ 16: begin alu_res = asr;        alu_sreg = set_lsr_flag; end
        /* LSR   */ 17: begin alu_res = lsr;        alu_sreg = set_lsr_flag; end
        /* ROR   */ 18: begin alu_res = ror;        alu_sreg = set_lsr_flag; end
        /* DEC   */ 19: begin alu_res = dec;        alu_sreg = set_dec_flag; end
        /* ADIW  */ 20: begin resw    = adiw;       alu_sreg = set_adiw_flag; end
        /* SBIW  */ 21: begin resw    = sbiw;       alu_sreg = set_sbiw_flag; end
        /* BLD   */ 22: begin
        case (op2[2:0])
            0: alu_res = {op1[7:1], sreg[6]};
            1: alu_res = {op1[7:2], sreg[6], op1[0]};
            2: alu_res = {op1[7:3], sreg[6], op1[1:0]};
            3: alu_res = {op1[7:4], sreg[6], op1[2:0]};
            4: alu_res = {op1[7:5], sreg[6], op1[3:0]};
            5: alu_res = {op1[7:6], sreg[6], op1[4:0]};
            6: alu_res = {op1[  7], sreg[6], op1[5:0]};
            7: alu_res = {          sreg[6], op1[6:0]};
        endcase
        end
        /* MUL   */ 23: begin resw = mul;   alu_sreg = set_mul_flag; end
        /* MULS  */ 24: begin resw = muls;  alu_sreg = set_muls_flag; end
        /* MULSU */ 25: begin resw = mulsu; alu_sreg = set_mulsu_flag; end
        default: alu_res = 8'hFF;
    endcase
end
endmodule