`include "opcodes.sv" module control( Function, SelInc, LoadPC, LoadIR, TrisOperand, TrisPC, TrisAcc, TrisMem, ENB, nME, ALE, RnW, nOE, Opcode, Zflag, nWait, Clock, nReset ); output [3:0] Function; output SelInc, LoadPC, LoadIR, TrisOperand, TrisPC, TrisAcc, TrisMem; output ENB, nME, ALE, RnW, nOE; input [3:0] Opcode; input Zflag, nWait, Clock, nReset; parameter Execute=0, Fetch=1; // // Gray code counting sequence for sub-state (0 1 3 2) // this ensures no glitches on the memory timing signals // and allows for a very simple implementation in hardware // parameter Addr_Setup=0, Addr_Hold=1, Data_Setup=3, Data_Hold=2; reg state; reg [1:0] sub_state; wire Branch, IncPC, Mem_Write; // // Define state machine // All instructions complete in exactly 8 clock cycles // state is Fetch or Execute // sub_state controls memory cycle // always_ff @(posedge Clock, negedge nReset) if (!nReset) // On reset this system will execute a NOP instruction // since state == 0 is execute and IR == 0 is NOP // It will then continue by fetching the first real // instruction from address 0 begin state <= 0; // Execute sub_state <= 0; // Addr_Setup end else begin if ( sub_state == Data_Hold ) if ( state == Fetch ) state <= #20ns Execute; else // Execute state <= #20ns Fetch; case (sub_state) Addr_Setup: sub_state <= #20ns Addr_Hold; Addr_Hold: sub_state <= #20ns Data_Setup; Data_Setup: if (nWait == 1) sub_state <= #20ns Data_Hold; Data_Hold: sub_state <= #20ns Addr_Setup; endcase end // // Identify memory write and generate memory control signals // assign Mem_Write = (Opcode == `STA) && (state == Execute); assign nME = ( sub_state == Addr_Setup ) || ( sub_state == Data_Hold ); assign ALE = ( sub_state == Addr_Setup ); assign RnW = ( sub_state == Addr_Setup ) || ( sub_state == Addr_Hold ) || ~Mem_Write; assign nOE = ( sub_state == Addr_Setup ) || ( sub_state == Addr_Hold ) || Mem_Write; assign ENB = ~nOE; // // Generate tri-state contol signals for SysBus // SysBus is driven by exactly one driver in each cycle // assign TrisOperand = ( state == Execute ) && (( sub_state == Addr_Setup ) || ( sub_state == Addr_Hold )); assign TrisPC = (state == Fetch) && (( sub_state == Addr_Setup ) || ( sub_state == Addr_Hold )); assign TrisAcc = (( sub_state == Data_Setup ) && Mem_Write) || ( sub_state == Data_Hold ); assign TrisMem = ( sub_state == Data_Setup ) && ~Mem_Write; // // Generate ALU control // assign Function = ((state == Execute) && (sub_state == Data_Setup) && (nWait == 1)) ? decodeFn(Opcode) : `FnACC; // // Identify successful control transfers and PC increment // assign Branch = (state == Execute) && ( sub_state == Data_Setup) && (nWait == 1) && ((Opcode ==`JMP) || ((Opcode ==`JMPZ) && (Zflag == 1)) || ((Opcode ==`JMPNZ) && (Zflag == 0)) ); assign IncPC = ((state == Fetch) && ( sub_state == Data_Hold )); // // Generate PC update multiplexor control signal // assign SelInc = IncPC; // // Generate Register Load Signals // assign LoadIR = ((state == Fetch) && (sub_state == Data_Setup) && (nWait == 1)); assign LoadPC = IncPC || Branch; // // Decode Opcode to create ALU Function code // function [3:0] decodeFn; input [3:0] opcode; case (opcode) `LDA : decodeFn = `FnMem; `ADD : decodeFn = `FnADD; `SUB : decodeFn = `FnSUB; `AND : decodeFn = `FnAND; `OR : decodeFn = `FnOR; `NOT : decodeFn = `FnNOT; `LSL : decodeFn = `FnLSL; `LSR : decodeFn = `FnLSR; default : decodeFn = `FnACC; endcase endfunction endmodule