/* 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_avr: AVR Core PC, SP, SREG, temp registers and control logic. */ `include "wbavr_consts.v" module wbavr_core( // avr common avr_clk_i, avr_reset_i, // avr code bus avr_c_adr_o, avr_c_dat_rd_i, avr_c_dat_wr_o, avr_c_rd_o, avr_c_wr_o, // avr data bus avr_d_adr_o, avr_d_dat_rd_i, avr_d_dat_wr_o, avr_d_rd_o, avr_d_wr_o, // interrupts irq_vect_i, irq_ack_o, // misc wdr_o ); parameter irq_vect_width; //----------------------------- // AVR I/O // avr clock and reset input avr_clk_i; input avr_reset_i; // avr code bus output [15:1] avr_c_adr_o; input [15:0] avr_c_dat_rd_i; output [15:0] avr_c_dat_wr_o; output avr_c_rd_o; output avr_c_wr_o; // avr data bus output [15:0] avr_d_adr_o; input [7:0] avr_d_dat_rd_i; output [7:0] avr_d_dat_wr_o; output avr_d_rd_o; output avr_d_wr_o; // interrupts input [irq_vect_width-1:0] irq_vect_i; output irq_ack_o; // misc output wdr_o; input [2:0] dadr_sel_i; //----------------------------- // ALU output [4:0] alu_cmd_o; output [7:0] alu_data_0_o; output [7:0] alu_data_1_o; output [7:0] alu_data_2_o; output [7:0] alu_flags_o; input [7:0] alu_data_0_i; input [7:0] alu_data_1_i; input [7:0] alu_flags_i; //----------------------------- // General Purpose Registers output [4:0] gp_rd_adr_0_o; input [7:0] gp_rd_data_0_i; output [4:0] gp_rd_adr_1_o; input [7:0] gp_rd_data_1_i; output gp_wr_enable_0_o; output [4:0] gp_wr_adr_0_o; output [7:0] gp_wr_data_0_o; output gp_wr_enable_1_o; output [4:0] gp_wr_adr_1_o; output [7:0] gp_wr_data_1_o; //----------------------------- // Opcode Decoder output [15:0] opcode_o; // opcode reg [15:0] opcode_o; output irq_req_o; // interrupt request output [irq_vect_width:0] irq_vect_o; // interrupt vector output [1:0] cycle_o; // cycle input reg [1:0] cycle_o; input alu_func_i; // ALU function input alu_i0_sel_i; // ALU parameter 0 selection input alu_i1_sel_i; // ALU parameter 1 selection input gp_rd_adr_0_i; // GP read register 0 address input gp_rd_adr_1_i; // GP read register 1 address input gp_wr_adr_0_i; // GP write register 0 address input gp_wr_adr_1_i; // GP write register 1 address input gp_wr_en_0_i; // GP write register 0 enable input gp_wr_en_1_i; // GP write register 1 enable input avr_cadr_sel_i; // AVR code bus address selection input avr_code_rd_i; // AVR code bus read input avr_code_wr_i; // AVR code bus write input avr_dadr_sel_i; // AVR data bus address selection input avr_ddat_sel_i; // AVR data bus write data selection input avr_data_rd_i; // AVR data bus read input avr_data_wr_i; // AVR data bus write input address_i; // address input interrupt_flag_i; // new value for the interrupt flag input flag_sel_i; // flag selection input pc_sel_i; // program counter selection input tmp_lo_sel_i; // temporary register low byte selection input tmp_hi_sel_i; // temporary register high byte selection input skip_sel_i; // skip selection input const1_i; // constant 1 from opcode input const2_i; // constant 2 from opcode input sleep_i; // sleep instruction input wdr_i; // watchdog reset instruction input break_i; // break instruction input irq_ack_i; // interrupt acknowledge input double_word_i; // double word instruction input illegal_op_i; // illegal opcode input [1:0] cycle_i; // next cycle output number //----------------------------- // Internals reg [15:0] program_counter; // program counter register (PC) reg [15:0] stack_pointer; // stack pointer register (SP) wire [15:0] stack_pointer_plus_minus_1; // stack pointer inc/decrease reg [7:0] flags; // flags register (SR) reg [7:0] tmp_hi; // temporary register high byte reg [7:0] tmp_lo; // temporary register low byte reg skip; reg [7:0] control; // control register reg [2:0] control_delay; // delay after which SPM is disabled wire control_write; // write to control register wire fetch_instr; // fetch instruction word when 1 wire fetch_addr; wire interrupt_request; reg [15:0] sp_next; assign stack_pointer_plus_minus_1 = (dadr_sel_i == `DADR_SEL_SP_INC) ? stack_pointer + 1 : stack_pointer - 1; assign interrupt_request = &{ flags[FLAG_I_IDX], |{ irq_vect_i } }; // fetch an instruction word when cycle is 0 or when skipping assign fetch_instr = |{ cycle_i == 2'b00, skip[0] }; // reading code bus when fetching an instruction or when opcode requests it assign avr_c_rd_o = |{ avr_code_rd_i, fetch_instr }; // code write (SPM) must be enabled assign avr_c_wr_o = &{ avr_code_wr_i, control[CTRL_PGWRT] }; always @( posedge avr_clk_i ) begin if( avr_reset_i ) begin program_counter <= program_counter_rst_addr; stack_pointer <= stack_pointer_rst_addr; // force a NOP opcode_o <= 0; cycle_o <= 2'b00; control <= 8'h00; control_delay <= 3'b000; end else begin // program_counter and instruction fetch if( fetch_instr ) begin opcode_o <= avr_c_dat_rd_i; program_counter <= program_counter + 1; end else begin program_counter <= pc_next; end // skip logic if( skip == 2'b00 && skip_next ) begin // skip next instruction skip <= 2'b01; end else if( skip == 2'b01 && double_word_i ) begin // double word instruction, skip another word skip <= 2'b11; end else begin skip <= 2'b00; end // control register if( control_write ) begin if( gp_rd_data_0_i[3:0] == 4'b0101 ) begin // enable SPM page write control <= gp_rd_data_0_i; control_delay <= 3'b100; end else begin // disable SPM control <= { gp_rd_data_0_i[7:4], 4'h0 }; control_delay <= 3'b000; end end else if( control[CTRL_SELFPRGEN] ) begin if( control_delay == 0 ) begin // disable SPM after delay control <= { gp_rd_data_0_i[7:4], 4'h0 }; end else begin control_delay <= control_delay - 1; end end // the rest... cycle_o <= cycle_i; flags <= flags_next; tmp_lo <= tmp_lo_next; tmp_hi <= tmp_hi_next; end end always @* begin // ALU input 0 case( alu_i0_sel_i ) `ALU_I0_SEL_REG0: alu_data_0_o <= gp_rd_data_0_i; `ALU_I0_SEL_FLAGS: alu_data_0_o <= flags; `ALU_I0_SEL_DATA: alu_data_0_o <= avr_d_dat_rd_i; `ALU_I0_SEL_CONST1: alu_data_0_o <= od_const_i; `ALU_I0_SEL_SP_LO: alu_data_0_o <= stack_pointer[7:0]; `ALU_I0_SEL_SP_HI: alu_data_0_o <= stack_pointer[15:8]; `ALU_I0_SEL_CTRL: alu_data_0_o <= control; endcase // ALU input 1 case( alu_i1_sel_i ) `ALU_I1_SEL_REG1: alu_data_1_o <= gp_rd_data_1_i; `ALU_I1_SEL_CONST1: alu_data_1_o <= od_const_i; `ALU_I1_SEL_TFLAG: alu_data_1_o <= { 7'h00, flags[FLAG_T_IDX] }; `ALU_I1_SEL_CODE: alu_data_1_o <= gp_rd_data_0_i[0] ? avr_c_rd_o[15:8] : avr_c_rd_o[7:0]; endcase // code adress case( cadr_sel_i ) `CADR_SEL_PC: avr_c_adr_o <= program_counter; `CADR_SEL_TMPREG: avr_c_adr_o <= { tmp_hi, tmp_lo }; `CADR_SEL_GPREG: avr_c_adr_o <= { 1'b0, gp_rd_data_1_i, gp_rd_data_0_i[7:1] }; endcase // data adress case( dadr_sel_i ) `DADR_SEL_OPCODE: avr_d_adr_o <= op_addr_i; `DADR_SEL_TMPREG: avr_d_adr_o <= { tmp_hi, tmp_lo }; `DADR_SEL_SP_DEC: avr_d_adr_o <= stack_pointer; `DADR_SEL_SP_INC: avr_d_adr_o <= stack_pointer_plus_minus_1; endcase // data write case( ddat_sel_i ) `DDAT_SEL_REG0: avr_d_dat_wr_o <= gp_rd_data_0_i; `DDAT_SEL_TMP_LO: avr_d_dat_wr_o <= tmp_lo; `DDAT_SEL_PC_LO: avr_d_dat_wr_o <= program_counter[7:0]; `DDAT_SEL_PC_HI: avr_d_dat_wr_o <= program_counter[15:8]; endcase // flags (SREG) case( flag_sel_i ) `FLAG_SEL_PREV: flags_next <= flags; `FLAG_SEL_ALU: flags_next <= alu_flags_i; `FLAG_SEL_O0: flags_next <= alu_data_0_i; `FLAG_SEL_CHG_I: flags_next <= { interrupt_flag_i, flags[6:0] }; endcase // tmp lo case( tmp_lo_sel_i ) `TMP_LO_SEL_PREV: tmp_lo_next <= tmp_lo; `TMP_LO_SEL_ALU_O0: tmp_lo_next <= alu_data_0_i; `TMP_LO_SEL_CODE_RD: tmp_lo_next <= avr_c_dat_rd_i[7:0]; endcase // tmp hi case( tmp_hi_sel_i ) `TMP_HI_SEL_PREV: tmp_hi_next <= tmp_hi; `TMP_HI_SEL_ALU_O1: tmp_hi_next <= alu_data_1_i; `TMP_HI_SEL_CODE_RD: tmp_hi_next <= avr_c_dat_rd_i[15:8]; `TMP_HI_SEL_DATA_RD: tmp_hi_next <= avr_d_dat_rd_i; endcase // program counter case( pc_sel_i ) `PC_SEL_PREV: pc_next <= program_counter; `PC_SEL_INC: pc_next <= program_counter + 1; `PC_SEL_ABSADDR: pc_next <= address_i; `PC_SEL_RELADDR: pc_next <= program_counter + address_i; `PC_SEL_CODE: pc_next <= avr_c_dat_rd_i; `PC_SEL_REG: pc_next <= { gp_rd_data_1_i, gp_rd_data_0_i }; `PC_SEL_THI_DLO: pc_next <= { tmp_hi, avr_d_dat_rd_i }; endcase // stack pointer case( sp_sel ) `SP_SEL_PREV: sp_next <= stack_pointer; `SP_SEL_INCDEC: sp_next <= stack_pointer_plus_minus_1; `SP_SEL_WR_LO: sp_next <= { stack_pointer[15:8], gp_rd_data_0_i }; `SP_SEL_WR_HI: sp_next <= { gp_rd_data_0_i, stack_pointer[7:0] }; endcase // skip case( skip_sel_i ) `SKIP_SEL_NONE: skip_next <= 0; `SKIP_SEL_Z_SET: skip_next <= alu_flags_i[FLAG_Z_IDX]; `SKIP_SEL_T_CLR: skip_next <= ~alu_flags_i[FLAG_Z_IDX]; `SKIP_SEL_T_SET: skip_next <= alu_flags_i[FLAG_Z_IDX]; endcase end endmodule