/*
Copyright (C) 2004 Pablo Bleyer Kocik.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
Behavioral KCPSMX implementation.
*/
`ifndef PACOBLAZE_V_
`define PACOBLAZE_V_
`include "pacoblaze_inc.v"
`include "pacoblaze_idu.v"
`include "pacoblaze_alu.v"
`include "pacoblaze_stack.v"
`ifdef HAS_MUL_OPERATION
`include "pacoblaze_dregister.v"
`else
`include "pacoblaze_register.v"
`endif
`ifdef HAS_SCRATCH_MEMORY
`include "pacoblaze_scratch.v"
`endif
/** Top PacoBlaze module */
module `PACOBLAZE(
address, instruction,
port_id,
write_strobe, out_port,
read_strobe, in_port,
interrupt,
`ifdef HAS_INTERRUPT_ACK
interrupt_ack,
`endif
reset, clk
);
output [`code_depth-1:0] address; ///< Address output
input [`code_width-1:0] instruction; ///< Instruction input
output [`port_depth-1:0] port_id; ///< Port address
output reg write_strobe; ///< Port output strobe
output [`port_width-1:0] out_port; ///< Port output
output reg read_strobe; ///< Port input strobe
input [`port_width-1:0] in_port; ///< Port input
input interrupt; ///< Interrupt request
`ifdef HAS_INTERRUPT_ACK
output interrupt_ack; ///< Interrupt acknowledge (output)
`endif
input reset; ///< Reset input
input clk; ///< Clock input
/* Processor registers and signals */
reg [`code_depth-1:0] program_counter; ///< Program counter
reg timing_control; ///< Timing control register
reg zero; ///< Zero flag
reg carry; ///< Carry flag
reg interrupt_enable; ///< Interrupt enable
reg interrupt_latch; ///< Interrupt latch hold
reg interrupt_ack; ///< Interrupt acknowledge
reg zero_saved; ///< Interrupt-saved zero flag
reg carry_saved; ///< Interrupt-saved carry flag
`ifdef HAS_RESET_LATCH
reg [1:0] reset_latch; ///< Reset latch
`endif
reg zero_carry_write_enable; ///< Zero/Carry update
wire internal_reset; ///< Internal reset signal
wire [`code_depth-1:0] program_counter_source, program_counter_next; ///< Next program counter
wire conditional_match; ///< True when unconditional or flags match
wire interrupt_assert; ///< True when interrupt condition is met
/* IDU - Instruction Decode Unit */
wire [`operation_width-1:0] idu_operation;
wire [2:0] idu_shift_operation;
wire idu_shift_direction, idu_shift_constant;
wire idu_operand_selection;
wire [`register_depth-1:0] idu_x_address, idu_y_address;
wire [`operand_width-1:0] idu_implied_value;
wire [`port_depth-1:0] idu_port_address;
`ifdef HAS_SCRATCH_MEMORY
wire [`scratch_depth-1:0] idu_scratch_address;
`endif
wire [`code_depth-1:0] idu_code_address;
wire idu_conditional;
wire [1:0] idu_condition_flags;
wire idu_interrupt_enable;
`ifdef HAS_DEBUG
wire [8*`debug_width:1] idu_debug_opcode;
`endif
`PACOBLAZE_IDU idu(
instruction,
idu_operation,
idu_shift_operation, idu_shift_direction, idu_shift_constant,
idu_operand_selection,
idu_x_address, idu_y_address,
idu_implied_value, idu_port_address,
`ifdef HAS_SCRATCH_MEMORY
idu_scratch_address,
`endif
idu_code_address,
idu_conditional, idu_condition_flags,
idu_interrupt_enable
`ifdef HAS_DEBUG
, idu_debug_opcode
`endif
);
/* ALU - Arithmetic-Logic Unit */
wire [`operand_width-1:0] alu_result, alu_operand_a, alu_operand_b;
`ifdef HAS_MUL_OPERATION
wire [2*`operand_width-1:0] alu_multiply;
`endif
wire alu_zero_out, alu_carry_out;
`PACOBLAZE_ALU alu(
idu_operation,
idu_shift_operation, idu_shift_direction, idu_shift_constant,
alu_result, alu_operand_a, alu_operand_b, carry,
`ifdef HAS_MUL_OPERATION
alu_multiply,
`endif
alu_zero_out, alu_carry_out
);
/* Register file */
reg register_x_write_enable;
wire [`register_width-1:0] register_x_data_in, register_x_data_out, register_y_data_out;
`PACOBLAZE_REGISTER register(
idu_x_address, register_x_write_enable, register_x_data_in, register_x_data_out,
idu_y_address, register_y_data_out,
reset, clk
);
/* Call/return stack */
wire stack_write_enable, stack_update_enable, stack_push_pop;
wire [`stack_width-1:0] stack_data_in = program_counter;
// (interrupt_latch == 0) ? program_counter_1 : program_counter; // wrong!
// (interrupt_assert) ? program_counter : program_counter_1;
wire [`stack_width-1:0] stack_data_out;
`PACOBLAZE_STACK stack(
stack_write_enable, stack_update_enable, stack_push_pop, stack_data_in, stack_data_out,
reset, clk
);
/* Scratchpad RAM */
`ifdef HAS_SCRATCH_MEMORY
reg scratch_write_enable;
wire [`scratch_depth-1:0] scratch_address =
(idu_operand_selection == 0) ? idu_scratch_address :
register_y_data_out[`scratch_depth-1:0];
wire [`scratch_width-1:0] scratch_data_out;
`PACOBLAZE_SCRATCH scratch(
scratch_address, scratch_write_enable, register_x_data_out, scratch_data_out,
reset, clk
);
`endif
/* Miscellaneous */
assign address = program_counter;
assign out_port = register_x_data_out;
assign port_id =
(idu_operand_selection == 0) ? idu_port_address : register_y_data_out;
`ifdef HAS_RESET_LATCH
assign internal_reset = reset_latch[1];
`else
assign internal_reset = reset;
`endif
assign conditional_match =
(!idu_conditional
|| idu_condition_flags == `flag_c && carry
|| idu_condition_flags == `flag_nc && ~carry
|| idu_condition_flags == `flag_z && zero
|| idu_condition_flags == `flag_nz && ~zero
) ? 1 : 0;
assign program_counter_source =
(interrupt_latch) ? `interrupt_vector :
(conditional_match && (idu_operation == `op_jump || idu_operation == `op_call)) ? idu_code_address :
(conditional_match && idu_operation == `op_return || idu_operation == `op_returni) ? stack_data_out :
program_counter;
assign program_counter_next =
(interrupt_latch ||
conditional_match && (idu_operation == `op_jump || idu_operation == `op_call)
|| idu_operation == `op_returni) ? program_counter_source :
program_counter_source + 1;
assign interrupt_assert = interrupt && interrupt_enable;
assign stack_write_enable = internal_reset || timing_control;
assign stack_update_enable = ~timing_control &&
(conditional_match && (idu_operation == `op_call || idu_operation == `op_return)
|| idu_operation == `op_returni
|| interrupt_latch);
// synthesis parallel_case full_case
assign stack_push_pop =
(conditional_match && idu_operation == `op_return || idu_operation == `op_returni) ? 0 : 1;
assign alu_operand_a = register_x_data_out;
assign alu_operand_b =
(idu_operand_selection == 0) ? idu_implied_value : register_y_data_out;
// synthesis parallel_case full_case
assign register_x_data_in =
`ifdef HAS_SCRATCH_MEMORY
(idu_operation == `op_fetch) ? scratch_data_out :
`endif
(idu_operation == `op_input) ? in_port : alu_result;
task decode;
input [`operation_width-1:0] operation;
begin
end
endtask
task execute;
input [`operation_width-1:0] operation;
begin
// synthesis parallel_case full_case
case (operation)
`op_load: register_x_write_enable <= 1;
`ifdef HAS_MUL_OPERATION
`op_mul,
`endif
`op_and,
`op_or,
`op_xor,
`op_add,
`op_addcy,
`op_sub,
`op_subcy,
`op_rs: begin
register_x_write_enable <= 1; // writeback sX
zero_carry_write_enable <= 1; // writeback zero, carry
end
/*
`op_jump: if (conditional_match) jump(program_counter_next);
`op_call: if (conditional_match) push(program_counter_next);
`op_return:
if (conditional_match) pop;
*/
`op_returni: begin
// pop;
zero <= zero_saved;
carry <= carry_saved;
interrupt_enable <= idu_interrupt_enable;
end
`op_interrupt: interrupt_enable <= idu_interrupt_enable;
`op_input: begin
read_strobe <= 1;
register_x_write_enable <= 1;
end
`op_output: write_strobe <= 1;
`ifdef HAS_COMPARE_OPERATION
`op_compare: zero_carry_write_enable <= 1;
`endif
`ifdef HAS_TEST_OPERATION
`op_test: zero_carry_write_enable <= 1;
`endif
`ifdef HAS_SCRATCH_MEMORY
`op_fetch: register_x_write_enable <= 1; // transfer scratch to sX
`op_store: scratch_write_enable <= 1; // transfer sX to scratch
`endif
default: ;
endcase
end
endtask
// combinatorial block
always @(program_counter) begin: com
// default actions
// program_counter_source =
// (idu_operation == `op_call)
// idu_code_address
// stack_data_out :
// program_counter;
//
// program_counter_next =
// () ? program_counter_source : program_counter_source + 1;
// stack_push_pop =
end
// sequential internal reset control
`ifdef HAS_RESET_LATCH
always @(posedge clk) begin: on_reset
if (reset) reset_latch <= 'b11;
else begin
reset_latch[1] <= reset_latch[0];
reset_latch[0] <= 0;
end
end
`endif
// sequential block
always @(posedge clk) begin: seq
/* Idle values and default actions */
read_strobe <= 0; write_strobe <= 0;
register_x_write_enable <= 0;
`ifdef HAS_SCRATCH_MEMORY
scratch_write_enable <= 0;
`endif
interrupt_ack <= 0;
zero_carry_write_enable <= 0;
if (internal_reset) begin: on_internal_reset
/* Reset values */
timing_control <= 0;
program_counter <= `reset_vector;
// stack_write_enable <= 1;
zero <= 0; carry <= 0;
interrupt_enable <= 0; interrupt_latch <= 0;
end
else begin: on_run
timing_control <= ~timing_control; // step timing control
// stack_write_enable <= ~timing_control;
interrupt_latch <= interrupt_assert;
// if (interrupt && interrupt_enable) interrupt_latch <= 1;
if (timing_control == 0) begin
program_counter <= program_counter_next; // default next program counter
if (interrupt_latch) begin // process interrupt
interrupt_enable <= 0; interrupt_latch <= 0; interrupt_ack <= 1;
zero_saved <= zero; carry_saved <= carry;
// push(`interrupt_vector);
end
else
execute(idu_operation);
end
else begin // timing_control == 1
if (zero_carry_write_enable) begin
zero <= alu_zero_out; carry <= alu_carry_out;
end
end
end // on_run
end
endmodule
`endif // PACOBLAZE_V_