/* Copyright (C) 2008 Martin Furter This file is part of wbavr. wbavr is free software/hardware; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. wbavr is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with wbavr; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ==============================================================================*/ /* wbavr_alu: AVR Arithmetic and Logic Unit. */ `include "wbavr_consts.v" `include "wbavr_multiply.v" module wbavr_alu( alu_cmd_i, // the ALU command to execute data_0_i, // input data 0 data_1_i, // input data 1 data_2_i, // input data 2 flags_i, // flags input (previous value) data_0_o, // output data 0 data_1_o, // output data 1 flags_o // flags output (new value) ); input [4:0] alu_cmd_i; input [7:0] data_0_i; input [7:0] data_1_i; input [7:0] data_2_i; input [7:0] flags_i; output [7:0] data_0_o; reg [7:0] data_0_o; output [7:0] data_1_o; reg [7:0] data_1_o; output [7:0] flags_o; // flags for the multiplication module: { float, b_signed, a_signed } reg [2:0] multiplication_flags; wire [7:0] product_lo; wire [7:0] product_hi; wire flag_I; wire flag_T_prev; reg flag_T; wire flag_H_prev; wire flag_H_add; wire flag_H_sub; reg flag_H; wire flag_S_prev; wire flag_S_dfl; reg flag_S; wire flag_V_prev; wire flag_V_add; wire flag_V_sub; wire flag_V_addw; wire flag_V_subw; wire flag_V_zero; wire flag_V_rshift; reg flag_V; wire flag_N_prev; wire flag_N_dfl; wire flag_N_addw; reg flag_N; wire flag_Z_prev; wire flag_Z_dfl; reg flag_Z; wire flag_C_prev; wire flag_C_add; wire flag_C_sub; wire flag_C_addw; wire flag_C_subw; wire flag_C_rshift; wire flag_C_one; wire flag_C_mul; reg flag_C; assign flags_o = { flag_I, flag_T, flag_H, flag_S, flag_V, flag_N, flag_Z, flag_C }; assign flag_I = flags_i[7]; assign flag_T_prev = flags_i[6]; // H: half carry // adiw, and, andi, asr, com, dec, eor, fmul, fmuls, fmulsu, mul, muls, mulsu, // inc, lsr, or, ori, ror, sbiw, swap assign flag_H_prev = flags_i[5]; // adc, add // Rd3*Rr3 + Rr3*/R3 + /R3*Rd3 assign flag_H_add = |{ &{ data_0_i[3], data_1_i[3] }, &{ data_1_i[3], !data_0_o[3] }, &{ !data_0_o[3], data_0_i[3] } }; // cp, cpc, cpi, neg, sbc, sbci, sub, subi // /Rd3*Rr3 + Rr3*R3 + R3*/Rd3 assign flag_H_sub = |{ &{ !data_0_i[3], data_1_i[3] }, &{ data_1_i[3], data_0_o[3] }, &{ data_0_o[3], !data_0_i[3] } }; // S: signed // fmul, fmuls, fmulsu, mul, muls, mulsu, swap assign flag_S_prev = flags_i[4]; // adc, add, adiw, and, andi, asr, com, cp, cpc, cpi, dec, inc, lsr, neg, // or, ori, ror, sbc, sbci, sbiw, sub, subi // N^V assign flag_S_dfl = flag_N ^ flag_V; // V: overflow // fmul, fmuls, fmulsu, mul, muls, mulsu, swap assign flag_V_prev = flags_i[3]; // adc, add, inc // Rd7*Rr7*/R7 + /Rd7*/Rr7*R7 assign flag_V_add = |{ &{ data_0_i[7], data_1_i[7], !data_0_o[7] }, &{ !data_0_i[7], !data_1_i[7], data_0_o[7] } }; // cp, cpc, cpi, dec, neg, sbc, sbci, sub, subi // Rd7*/Rr7*/R7 + /Rd7*Rr7*R7 assign flag_V_sub = |{ &{ data_0_i[7], !data_1_i[7], !data_0_o[7] }, &{ !data_0_i[7], data_1_i[7], data_0_o[7] } }; // adiw // /Rdh7*R15 assign flag_V_addw = &{ !data_1_i[7], data_1_o[7] }; // sbiw // Rdh7*/R15 assign flag_V_subw = &{ data_1_i[7], !data_1_o[7] }; // and, andi, com, eor, or, ori // =0 assign flag_V_zero = 1'b0; // asr, lsr, ror // N^C assign flag_V_rshift = flag_N ^ flag_C; // N: negative // fmul, fmuls, fmulsu, mul, muls, mulsu, swap assign flag_N_prev = flags_i[2]; // adc, add, and, andi, asr, com, cp, cpc, cpi, dec, eor, inc, lsr, neg, // or, ori, ror, sbc, sbci, sub, subi assign flag_N_dfl = data_0_o[7]; // adiw, sbiw assign flag_N_addw = data_1_o[7]; // Z: zero // - assign flag_Z_prev = flags_i[1]; // adc, add, and, andi, asr, com, cp, cpc, cpi, dec, eor, inc, lsr, neg, // or, ori, ror, sbc, sbci, sub, subi, swap, // adiw, fmul, fmuls, fmulsu, mul, muls, mulsu, sbiw assign flag_Z_dfl = ~|{ data_0_o, data_1_o }; // C: carry // and, andi, dec, eor, inc, or, ori, swap assign flag_C_prev = flags_i[0]; // adc, add // Rd7*Rr7 + Rr7*R7 + R7*Rd7 assign flag_C_add = |{ &{ data_0_i[7], data_1_i[7] }, &{ data_1_i[7], !data_0_o[7] }, &{ !data_0_o[7], data_0_i[7] } }; // cp, cpc, cpi, neg, sbc, sbci, sub, subi // /Rd7*Rr7 + Rr7*R7 + R7*/Rd7 assign flag_C_sub = |{ &{ !data_0_i[7], data_1_i[7] }, &{ data_1_i[7], data_0_o[7] }, &{ data_0_o[7], !data_0_i[7] } }; // adiw // /R15*Rdh7 assign flag_C_addw = &{ !data_1_o[7], data_1_i[7] }; // sbiw // R15*/Rdh7 assign flag_C_subw = &{ data_1_o[7], !data_1_i[7] }; // asr, lsr, ror // Rd0 assign flag_C_rshift = data_0_i[0]; // com // =1 assign flag_C_one = 1'b1; // fmul, fmuls, fmulsu, mul, muls, mulsu // flag_C_mul from wbavr_multiply always @* begin case( alu_cmd_i ) // one input, one output `ALU_ASR: begin data_0_o <= { data_0_i[7], data_0_i[7:1] }; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_rshift; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_rshift; end `ALU_LSR: begin data_0_o <= { 1'b0, data_0_i[7:1] }; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_rshift; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_rshift; end `ALU_ROR: begin data_0_o <= { flag_C_prev, data_0_i[7:1] }; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_rshift; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_rshift; end `ALU_SWAP: begin data_0_o <= { data_0_i[3:0], data_0_i[7:4] }; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_prev; end // two inputs, one output `ALU_ADC: begin data_0_o <= data_0_i + data_1_i + { 7'b0000_000, flag_C_prev }; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_add; flag_S <= flag_S_dfl; flag_V <= flag_V_add; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_add; end `ALU_ADD, `ALU_INC: begin data_0_o <= data_0_i + data_1_i; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; if( alu_cmd_i == `ALU_ADD ) begin flag_H <= flag_H_add; flag_C <= flag_C_add; end else begin flag_H <= flag_H_prev; flag_C <= flag_C_prev; end flag_T <= flag_T_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_add; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; end `ALU_AND: begin data_0_o <= data_0_i & data_1_i; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_zero; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_prev; end `ALU_EOR: begin data_0_o <= data_0_i ^ data_1_i; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_zero; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_prev; end `ALU_OR: begin data_0_o <= data_0_i | data_1_i; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_zero; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_prev; end `ALU_SBC: begin data_0_o <= data_0_i - data_1_i - flag_C_prev; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_sub; flag_S <= flag_S_dfl; flag_V <= flag_V_sub; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; flag_C <= flag_C_sub; end `ALU_SUB, `ALU_DEC, `ALU_COM: begin data_0_o <= data_0_i - data_1_i; data_1_o <= 8'b0000_0000; multiplication_flags <= 3'b000; if( alu_cmd_i == `ALU_SUB ) begin flag_H <= flag_H_sub; flag_C <= flag_C_sub; end else if( alu_cmd_i == `ALU_DEC ) begin flag_H <= flag_H_prev; flag_C <= flag_C_prev; end else begin flag_H <= flag_H_prev; flag_C <= flag_C_one; end flag_T <= flag_T_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_sub; flag_N <= flag_N_dfl; flag_Z <= flag_Z_dfl; end // two inputs, two outputs `ALU_FMUL: begin data_0_o <= product_lo; data_1_o <= product_hi; multiplication_flags <= 3'b100; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_mul; end `ALU_FMULS: begin data_0_o <= product_lo; data_1_o <= product_hi; multiplication_flags <= 3'b111; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_mul; end `ALU_FMULSU: begin data_0_o <= product_lo; data_1_o <= product_hi; multiplication_flags <= 3'b110; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_mul; end `ALU_MUL: begin data_0_o <= product_lo; data_1_o <= product_hi; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_mul; end `ALU_MULS: begin data_0_o <= product_lo; data_1_o <= product_hi; multiplication_flags <= 3'b011; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_mul; end `ALU_MULSU: begin data_0_o <= product_lo; data_1_o <= product_hi; multiplication_flags <= 3'b010; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_dfl; flag_C <= flag_C_mul; end // three inputs, two outputs `ALU_ADDW: begin { data_1_o, data_0_o } <= { data_1_i, data_0_i } + { 8'b0000_0000, data_2_i }; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_addw; flag_N <= flag_N_addw; flag_Z <= flag_Z_dfl; flag_C <= flag_C_addw; end `ALU_SUBW: begin { data_1_o, data_0_o } <= { data_1_i, data_0_i } - { 8'b0000_0000, data_2_i }; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_dfl; flag_V <= flag_V_subw; flag_N <= flag_N_addw; flag_Z <= flag_Z_dfl; flag_C <= flag_C_subw; end // bit operations `ALU_BITOP: begin case( data_2_i[2:0] ) 3'b000: begin data_0_o <= { data_0_i[7:1], data_1_i[0] }; end 3'b001: begin data_0_o <= { data_0_i[7:2], data_1_i[1], data_1_i[0] }; end 3'b010: begin data_0_o <= { data_0_i[7:3], data_1_i[2], data_1_i[1:0] }; end 3'b011: begin data_0_o <= { data_0_i[7:4], data_1_i[3], data_1_i[2:0] }; end 3'b100: begin data_0_o <= { data_0_i[7:5], data_1_i[4], data_1_i[3:0] }; end 3'b101: begin data_0_o <= { data_0_i[7:6], data_1_i[5], data_1_i[4:0] }; end 3'b110: begin data_0_o <= { data_0_i[7], data_1_i[6], data_1_i[5:0] }; end 3'b111: begin data_0_o <= { data_1_i[7], data_1_i[6:0] }; end endcase // data_1_o <= { 7'b0000_000, data_0_i[ data_2_i[2:0] ] }; data_1_o = 8'b0000_0000; multiplication_flags <= 3'b000; flag_T <= data_0_i[ data_2_i[2:0] ]; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_prev; flag_C <= flag_C_prev; end // `ALU_NOP, default: begin data_0_o <= data_0_i; data_1_o <= data_1_i; multiplication_flags <= 3'b000; flag_T <= flag_T_prev; flag_H <= flag_H_prev; flag_S <= flag_S_prev; flag_V <= flag_V_prev; flag_N <= flag_N_prev; flag_Z <= flag_Z_prev; flag_C <= flag_C_prev; end endcase end wbavr_multiply U0( .a_i(data_0_i), .a_signed_i(multiplication_flags[0]), .b_i(data_0_i), .b_signed_i(multiplication_flags[1]), .float_i(multiplication_flags[2]), .product_lo_o(product_lo), .product_hi_o(product_hi), .carry_o(flag_C_mul) ); endmodule