Line 46... |
Line 46... |
//// ////
|
//// ////
|
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
|
|
`timescale 1ns / 1ps
|
`timescale 1ns / 1ps
|
|
|
module t6507lp_fsm(clk_in, n_rst_in, alu_result, alu_status, data_in, address, control, data_out, alu_opcode, alu_a);
|
module t6507lp_fsm(clk_in, rst_in_n, alu_result, alu_status, data_in, address, control, data_out, alu_opcode, alu_a, alu_enable);
|
|
|
|
parameter DATA_SIZE = 4'd8;
|
|
parameter ADDR_SIZE = 4'd13;
|
|
|
input clk_in;
|
input clk_in;
|
input n_rst_in;
|
input rst_in_n;
|
input [7:0] alu_result;
|
input [DATA_SIZE-1:0] alu_result;
|
input [7:0] alu_status;
|
input [DATA_SIZE-1:0] alu_status;
|
input [7:0] data_in;
|
input [DATA_SIZE-1:0] data_in;
|
output reg [12:0] address;
|
output reg [ADDR_SIZE-1:0] address;
|
output reg control; // one bit is enough? read = 0, write = 1
|
output reg control; // one bit is enough? read = 0, write = 1
|
output reg [7:0] data_out;
|
output reg [DATA_SIZE-1:0] data_out;
|
output reg [7:0] alu_opcode;
|
output reg [DATA_SIZE-1:0] alu_opcode;
|
output reg [7:0] alu_a;
|
output reg [DATA_SIZE-1:0] alu_a;
|
|
output reg alu_enable;
|
|
|
|
|
// FSM states
|
// FSM states
|
|
localparam RESET = 4'b1111;
|
localparam FETCH_OP = 4'b0000;
|
localparam FETCH_OP = 4'b0000;
|
localparam FETCH_LOW = 4'b0001;
|
localparam FETCH_OP_CALC = 4'b0001;
|
localparam FETCH_HIGH = 4'b0010;
|
localparam FETCH_LOW = 4'b0010;
|
localparam SET_PC = 4'b0011;
|
localparam FETCH_HIGH = 4'b0011;
|
localparam READ_EFFECTIVE = 4'b0100;
|
|
localparam DO_OPERATION = 4'b0101;
|
|
localparam WRITE_DUMMY = 4'b0110;
|
|
localparam WRITE_EFFECTIVE = 4'b0111;
|
|
localparam CALCULATE_INDEX = 4'b1000;
|
|
localparam CHECK_FOR_PAGE_CROSS = 4'b1001;
|
|
|
|
// OPCODES TODO: verify how this get synthesised
|
// OPCODES TODO: verify how this get synthesised
|
`include "../T6507LP_Package.v"
|
`include "../T6507LP_Package.v"
|
|
|
// control signals
|
// control signals
|
localparam MEM_READ = 1'b0;
|
localparam MEM_READ = 1'b0;
|
localparam MEM_WRITE = 1'b1;
|
localparam MEM_WRITE = 1'b1;
|
|
|
reg [12:0] pc; // program counter
|
reg [ADDR_SIZE-1:0] pc; // program counter
|
reg [7:0] sp; // stack pointer
|
reg [DATA_SIZE-1:0] sp; // stack pointer
|
reg [7:0] ir; // instruction register
|
reg [DATA_SIZE-1:0] ir; // instruction register
|
reg [12:0] temp_add; // temporary address
|
reg [ADDR_SIZE:0] temp_add; // temporary address
|
reg [7:0] temp_data; // temporary data
|
reg [DATA_SIZE-1:0] temp_data; // temporary data
|
|
|
reg [3:0] state, next_state; // current and next state registers
|
reg [3:0] state, next_state; // current and next state registers
|
// TODO: not sure if this will be 4 bits wide. as of march 9th this was 4bit wide.
|
// TODO: not sure if this will be 4 bits wide. as of march 9th this was 4bit wide.
|
|
|
// wiring that simplifies the FSM logic
|
// wiring that simplifies the FSM logic
|
Line 103... |
Line 104... |
reg read;
|
reg read;
|
reg read_modify_write;
|
reg read_modify_write;
|
reg write;
|
reg write;
|
reg jump;
|
reg jump;
|
|
|
reg enable;
|
wire [ADDR_SIZE-1:0] next_pc;
|
|
|
wire [12:0] next_pc;
|
|
assign next_pc = pc + 13'b0000000000001;
|
assign next_pc = pc + 13'b0000000000001;
|
|
|
always @ (posedge clk_in or negedge n_rst_in) begin
|
always @ (posedge clk_in or negedge rst_in_n) begin
|
if (n_rst_in == 1'b0) begin
|
if (rst_in_n == 1'b0) begin
|
// TODO: all registers must assume default values
|
// TODO: all internal flip-flops must assume default values
|
|
|
pc <= {13{1'b0}}; // TODO: this is written somewhere. something about a reset vector. must be checked.
|
pc <= 0; // TODO: this is written somewhere. something about a reset vector. must be checked.
|
sp <= 8'h00; // TODO: the default is not 0. maybe $0100 or something like that. must be checked.
|
sp <= 0; // TODO: the default is not 0. maybe $0100 or something like that. must be checked.
|
ir <= 8'h00;
|
ir <= 0;
|
temp_add <= {13{1'b0}};
|
temp_add <= 0;
|
temp_data <= {8{1'b0}};
|
temp_data <= 0;
|
state <= FETCH_OP;
|
state <= RESET;
|
|
|
//address <= {13{1'b0}};
|
|
//control <= 1'b0;
|
|
//data_out <= {8{1'b0}};
|
|
//alu_opcode <= {8{1'b0}};
|
|
//alu_a <= {8{1'b0}};
|
|
end
|
end
|
else begin
|
else begin
|
state <= next_state;
|
state <= next_state;
|
|
|
address <= pc; // this secures the pipelining will happen by default
|
|
|
|
case (state)
|
case (state)
|
|
RESET: begin
|
|
// The processor was reset
|
|
end
|
FETCH_OP: begin // this state is the simplest one. it is a simple fetch that must be done when the cpu was reset or
|
FETCH_OP: begin // this state is the simplest one. it is a simple fetch that must be done when the cpu was reset or
|
// the last cycle was a memory write.
|
// the last cycle was a memory write.
|
pc <= next_pc;
|
pc <= next_pc;
|
end
|
end
|
FETCH_LOW: begin // in this state the opcode is already known so truly execution begins
|
FETCH_OP_CALC: begin // this is the pipeline happening!
|
pc <= next_pc;
|
|
ir <= data_in; // opcode must be saved in the instruction register. this is not necessary for the IMP and ACC modes.
|
|
end
|
|
FETCH_HIGH: begin
|
|
pc <= next_pc;
|
pc <= next_pc;
|
|
|
temp_add <= {5'b00000, data_in}; // data from previous cycle (lowbyte) is ready and must be saved
|
|
end
|
|
SET_PC: begin
|
|
pc <= {data_in[4:0], temp_add[7:0]};
|
|
end
|
|
READ_EFFECTIVE: begin
|
|
if (zero_page) begin
|
|
temp_add <= {5'b00000, data_in}; // this is necessary for the write_effective state
|
|
address <= {5'b00000, data_in};
|
|
end
|
|
else if (zero_page_indexed) begin
|
|
temp_add <= {5'b00000, alu_result}; // this is necessary for the write_effective state
|
|
address <= {5'b00000, alu_result};
|
|
end
|
|
else begin
|
|
temp_add[12:8] <= data_in;
|
|
address <= {data_in[4:0], temp_add[7:0]};
|
|
end
|
|
end
|
|
DO_OPERATION, CALCULATE_INDEX: begin
|
|
end
|
|
WRITE_DUMMY: begin
|
|
//if (zero_page) begin // i believe this works fine
|
|
|
|
address <= temp_add;
|
|
//control <= WRITE; // just for compatibility
|
|
//alu_opcode <= ir;
|
|
//alu_a <= data_in;
|
|
end
|
end
|
WRITE_EFFECTIVE: begin
|
FETCH_LOW: begin // in this state the opcode is already known so truly execution begins
|
if (zero_page) begin
|
if (accumulator || implied) begin
|
address <= temp_add;
|
pc <= pc; // is this necessary?
|
end
|
|
else if (zero_page_indexed) begin
|
|
address <= {5'b00000, alu_result};
|
|
end
|
|
else begin // maybe this could be rearenged for better synth
|
|
if (write) begin
|
|
address <= {data_in[4:0], temp_add[7:0]};
|
|
end
|
end
|
else begin
|
else begin
|
address <= temp_add;
|
pc <= next_pc;
|
end
|
ir <= data_in; // opcode must be saved in the instruction register
|
end
|
|
|
|
//data_out <= alu_result;
|
|
//control <= WRITE;
|
|
end
|
end
|
CHECK_FOR_PAGE_CROSS: begin
|
|
temp_add[12:8] <= data_in;
|
|
address <= {data_in[4:0], temp_add[7:0]};
|
|
end
|
end
|
default: begin
|
default: begin
|
state <= FETCH_OP; // TODO: this prevents the system from halting but may trigger strange behavior.
|
$write("unknown state"); // TODO: check if synth really ignores this 2 lines. Otherwise wrap it with a `ifdef
|
$finish("UNKNOWN STATE!"); // TODO: check if synth really ignores this line. Otherwise wrap it with a `ifdef
|
$finish(0);
|
end
|
end
|
|
|
endcase
|
endcase
|
end
|
end
|
end
|
end
|
|
|
always @(posedge clk_in) begin // combinational block that handles the outputs
|
always @ (*) begin // this is the next_state logic and output logic always block
|
enable <= 1'b0;
|
address = pc;
|
control <= MEM_READ;
|
control = MEM_READ;
|
alu_opcode <= 8'h00;
|
data_out = 8'h00;
|
alu_a <= 8'h00;
|
alu_opcode = 8'h00;
|
data_out <= 8'h00;
|
alu_a = 8'h00;
|
|
alu_enable = 1'b0;
|
case (state)
|
|
FETCH_OP, SET_PC, READ_EFFECTIVE: begin
|
|
//enable = 0;
|
|
end
|
|
FETCH_LOW: begin
|
|
if (accumulator || implied) begin // the ALU must be used
|
|
enable <= 1'b1;
|
|
alu_opcode <= data_in;
|
|
end
|
|
end
|
|
FETCH_HIGH: begin
|
|
if (immediate || write || absolute_indexed) begin
|
|
enable <= 1'b1;
|
|
alu_opcode <= ir;
|
|
alu_a <= data_in;
|
|
end
|
|
end
|
|
DO_OPERATION, CALCULATE_INDEX: begin
|
|
enable <= 1'b1;
|
|
alu_opcode <= ir;
|
|
alu_a <= data_in;
|
|
end
|
|
WRITE_DUMMY: begin
|
|
//if (zero_page) begin // i believe this works fine
|
|
control <= MEM_WRITE; // just for compatibility
|
|
data_out <= data_in; // just for compatibility
|
|
alu_opcode <= ir;
|
|
alu_a <= data_in;
|
|
end
|
|
WRITE_EFFECTIVE: begin
|
|
control <= MEM_WRITE;
|
|
data_out <= alu_result;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
always @ (*) begin // this is the next_state always
|
|
|
|
next_state = FETCH_OP; // this should avoid latch inferring
|
next_state = RESET; // this prevents the latch
|
|
|
begin
|
begin
|
case (state)
|
case (state)
|
FETCH_OP: begin
|
RESET: begin
|
next_state = FETCH_LOW;
|
next_state = FETCH_OP;
|
end
|
|
FETCH_LOW: begin
|
|
if (accumulator || implied) begin
|
|
next_state = FETCH_OP; // not sure
|
|
end
|
|
else if (zero_page) begin // zero page behaves exactly as absolute except that it has only the first fetch
|
|
if (read || read_modify_write) begin
|
|
next_state = READ_EFFECTIVE;
|
|
end
|
|
else if (write) begin
|
|
next_state = WRITE_EFFECTIVE;
|
|
end
|
|
end
|
|
else if (zero_page_indexed) begin
|
|
next_state = CALCULATE_INDEX;
|
|
end
|
|
else begin
|
|
next_state = FETCH_HIGH;
|
|
end
|
|
end
|
end
|
FETCH_HIGH: begin
|
FETCH_OP: begin
|
if (immediate) begin
|
|
next_state = FETCH_LOW;
|
next_state = FETCH_LOW;
|
end
|
end
|
else if (absolute) begin
|
FETCH_OP_CALC: begin
|
if (jump) begin
|
|
next_state = SET_PC;
|
|
end
|
|
else if (read || read_modify_write) begin // TODO: verify if this should stay like this
|
|
// (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, LAX, NOP) reads
|
|
// (ASL, LSR, ROL, ROR, INC, DEC, SLO, SRE, RLA, RRA, ISB, DCP) modifs
|
|
// (STA, STX, STY, SAX) writes
|
|
next_state = READ_EFFECTIVE;
|
|
end
|
|
else if (write) begin
|
|
next_state = WRITE_EFFECTIVE;
|
|
end
|
|
end
|
|
else if (absolute_indexed) begin
|
|
next_state = CHECK_FOR_PAGE_CROSS;
|
|
end
|
|
end
|
|
SET_PC, DO_OPERATION: begin
|
|
next_state = FETCH_LOW;
|
next_state = FETCH_LOW;
|
|
alu_opcode = ir;
|
|
alu_enable = 1'b1;
|
end
|
end
|
READ_EFFECTIVE: begin
|
FETCH_LOW: begin
|
if (read) begin
|
if (accumulator || implied) begin
|
next_state = DO_OPERATION;
|
alu_opcode = data_in;
|
end
|
alu_enable = 1'b1;
|
else if (read_modify_write) begin
|
|
next_state = WRITE_DUMMY;
|
|
end
|
|
end
|
|
WRITE_EFFECTIVE: begin
|
|
next_state = FETCH_OP;
|
next_state = FETCH_OP;
|
end
|
end
|
CALCULATE_INDEX: begin
|
else if (immediate) begin
|
if (read || read_modify_write) begin
|
next_state = FETCH_OP_CALC;
|
next_state = READ_EFFECTIVE;
|
|
end
|
|
else if (write) begin
|
|
next_state = WRITE_EFFECTIVE;
|
|
end
|
|
end
|
|
CHECK_FOR_PAGE_CROSS: begin
|
|
if (alu_status[V] == 1'b0) begin // check the overflow bit
|
|
if (read || read_modify_write) begin
|
|
next_state = READ_EFFECTIVE;
|
|
end
|
|
else begin
|
|
next_state = WRITE_EFFECTIVE;
|
|
end
|
|
end
|
|
else begin // TODO: this is totally uncertain. absolute indexed mode must be reviewed
|
|
if (read || read_modify_write) begin
|
|
next_state = DO_OPERATION;
|
|
end
|
end
|
else begin
|
else begin // at least the immediate address mode falls here
|
next_state = WRITE_EFFECTIVE;
|
next_state = FETCH_HIGH;
|
end
|
end
|
end
|
end
|
|
default: begin
|
|
next_state = RESET;
|
end
|
end
|
//end
|
|
|
|
endcase
|
endcase
|
end
|
end
|
end
|
end
|
|
|
// this always block is responsible for updating the address mode
|
// this always block is responsible for updating the address mode
|
always @ (*) begin // TODO: the sensitivity may not be correct
|
always @ (*) begin //
|
|
|
absolute = 1'b0;
|
absolute = 1'b0;
|
absolute_indexed = 1'b0;
|
absolute_indexed = 1'b0;
|
accumulator = 1'b0;
|
accumulator = 1'b0;
|
immediate = 1'b0;
|
immediate = 1'b0;
|
implied = 1'b0;
|
implied = 1'b0;
|
Line 361... |
Line 210... |
read = 1'b0;
|
read = 1'b0;
|
read_modify_write = 1'b0;
|
read_modify_write = 1'b0;
|
write = 1'b0;
|
write = 1'b0;
|
jump = 1'b0;
|
jump = 1'b0;
|
|
|
if (state == FETCH_LOW) begin // TODO: does this works?
|
if (state == FETCH_LOW) begin
|
case (data_in)
|
case (data_in)
|
BRK_IMP, CLC_IMP, CLD_IMP, CLI_IMP, CLV_IMP, DEX_IMP, DEY_IMP, INX_IMP, INY_IMP, NOP_IMP, PHA_IMP, PHP_IMP, PLA_IMP,
|
BRK_IMP, CLC_IMP, CLD_IMP, CLI_IMP, CLV_IMP, DEX_IMP, DEY_IMP, INX_IMP, INY_IMP, NOP_IMP, PHA_IMP, PHP_IMP, PLA_IMP,
|
PLP_IMP, RTI_IMP, RTS_IMP, SEC_IMP, SED_IMP, SEI_IMP, TAX_IMP, TAY_IMP, TSX_IMP, TXA_IMP, TXS_IMP, TYA_IMP: begin
|
PLP_IMP, RTI_IMP, RTS_IMP, SEC_IMP, SED_IMP, SEI_IMP, TAX_IMP, TAY_IMP, TSX_IMP, TXA_IMP, TXS_IMP, TYA_IMP: begin
|
implied = 1'b1;
|
implied = 1'b1;
|
end
|
end
|