2719 lines
78 KiB
Systemverilog
2719 lines
78 KiB
Systemverilog
//
|
|
// FX68K
|
|
//
|
|
// M68000 cycle accurate, fully synchronous
|
|
// Copyright (c) 2018,2021 by Jorge Cwik
|
|
//
|
|
// TODO:
|
|
// - Everything except bus retry already implemented.
|
|
|
|
// altera message_off 10030
|
|
// altera message_off 10230
|
|
|
|
`timescale 1 ns / 1 ns
|
|
|
|
`define USE_E_CLKEN
|
|
/*
|
|
Define USE_E_CLKEN will output two signals that generate a single cycle pulse just before the raising and falling edges of E.
|
|
Use this when you need to generate changes that must be simultaneous with these edges.
|
|
Most systems don't need this. Note that these signals are not registered.
|
|
*/
|
|
|
|
// Define this to run a self contained compilation test build
|
|
// `define FX68K_TEST
|
|
|
|
localparam CF = 0, VF = 1, ZF = 2, NF = 3, XF = 4, SF = 13;
|
|
|
|
localparam UADDR_WIDTH = 10;
|
|
localparam UROM_WIDTH = 17;
|
|
localparam UROM_DEPTH = 1024;
|
|
|
|
localparam NADDR_WIDTH = 9;
|
|
localparam NANO_WIDTH = 68;
|
|
localparam NANO_DEPTH = 336;
|
|
|
|
localparam BSER1_NMA = 'h003;
|
|
localparam RSTP0_NMA = 'h002;
|
|
localparam HALT1_NMA = 'h001;
|
|
localparam TRAC1_NMA = 'h1C0;
|
|
localparam ITLX1_NMA = 'h1C4;
|
|
|
|
localparam TVN_SPURIOUS = 12;
|
|
localparam TVN_AUTOVEC = 13;
|
|
localparam TVN_INTERRUPT = 15;
|
|
|
|
localparam NANO_DOB_DBD = 2'b01;
|
|
localparam NANO_DOB_ADB = 2'b10;
|
|
localparam NANO_DOB_ALU = 2'b11;
|
|
|
|
|
|
// Clocks, phases and resets
|
|
typedef struct {
|
|
logic clk;
|
|
logic extReset; // External sync reset on emulated system
|
|
logic pwrUp; // Asserted together with reset on emulated system coldstart
|
|
logic enPhi1, enPhi2; // Clock enables. Next cycle is PHI1 or PHI2
|
|
} s_clks;
|
|
|
|
// IRD decoded signals
|
|
typedef struct {
|
|
logic isPcRel;
|
|
logic isTas;
|
|
logic implicitSp;
|
|
logic toCcr;
|
|
logic rxIsDt, ryIsDt;
|
|
logic rxIsUsp, rxIsMovem, movemPreDecr;
|
|
logic isByte;
|
|
logic isMovep;
|
|
logic [2:0] rx, ry;
|
|
logic rxIsAreg, ryIsAreg;
|
|
logic [15:0] ftuConst;
|
|
logic [5:0] macroTvn;
|
|
logic inhibitCcr;
|
|
} s_irdecod;
|
|
|
|
// Nano code decoded signals
|
|
typedef struct {
|
|
logic permStart;
|
|
logic waitBusFinish;
|
|
logic isWrite;
|
|
logic busByte;
|
|
logic isRmc;
|
|
logic noLowByte, noHighByte;
|
|
|
|
logic updTpend, clrTpend;
|
|
logic tvn2Ftu, const2Ftu;
|
|
logic ftu2Dbl, ftu2Abl;
|
|
logic abl2Pren, updPren;
|
|
logic inl2psw, ftu2Sr, sr2Ftu, ftu2Ccr, pswIToFtu;
|
|
logic ird2Ftu, ssw2Ftu;
|
|
logic initST;
|
|
logic Ir2Ird;
|
|
|
|
logic auClkEn, noSpAlign;
|
|
logic [2:0] auCntrl;
|
|
logic todbin, toIrc;
|
|
logic dbl2Atl, abl2Atl, atl2Abl, atl2Dbl;
|
|
logic abh2Ath, dbh2Ath;
|
|
logic ath2Dbh, ath2Abh;
|
|
|
|
logic db2Aob, ab2Aob, au2Aob;
|
|
logic aob2Ab, updSsw;
|
|
// logic adb2Dob, dbd2Dob, alu2Dob;
|
|
logic [1:0] dobCtrl;
|
|
|
|
logic abh2reg, abl2reg;
|
|
logic reg2abl, reg2abh;
|
|
logic dbh2reg, dbl2reg;
|
|
logic reg2dbl, reg2dbh;
|
|
logic ssp, pchdbh, pcldbl, pclabl, pchabh;
|
|
|
|
logic rxh2dbh, rxh2abh;
|
|
logic dbl2rxl, dbh2rxh;
|
|
logic rxl2db, rxl2ab;
|
|
logic abl2rxl, abh2rxh;
|
|
logic dbh2ryh, abh2ryh;
|
|
logic ryl2db, ryl2ab;
|
|
logic ryh2dbh, ryh2abh;
|
|
logic dbl2ryl, abl2ryl;
|
|
logic rz;
|
|
logic rxlDbl;
|
|
|
|
logic [2:0] aluColumn;
|
|
logic [1:0] aluDctrl;
|
|
logic aluActrl;
|
|
logic aluInit, aluFinish;
|
|
logic abd2Dcr, dcr2Dbd;
|
|
logic dbd2Alue, alue2Dbd;
|
|
logic dbd2Alub, abd2Alub;
|
|
|
|
logic alu2Dbd, alu2Abd;
|
|
logic au2Db, au2Ab, au2Pc;
|
|
logic dbin2Abd, dbin2Dbd;
|
|
logic extDbh, extAbh;
|
|
logic ablAbd, ablAbh;
|
|
logic dblDbd, dblDbh;
|
|
logic abdIsByte;
|
|
} s_nanod;
|
|
|
|
module fx68k(
|
|
input clk,
|
|
input HALTn, // Used for single step only. Force high if not used
|
|
// input logic HALTn = 1'b1, // Not all tools support default port values
|
|
|
|
// These two signals don't need to be registered. They are not async reset.
|
|
input extReset, // External sync reset on emulated system
|
|
input pwrUp, // Asserted together with reset on emulated system coldstart
|
|
input enPhi1, enPhi2, // Clock enables. Next cycle is PHI1 or PHI2
|
|
|
|
output eRWn, output ASn, output LDSn, output UDSn,
|
|
output logic E, output VMAn,
|
|
|
|
`ifdef USE_E_CLKEN
|
|
// divide the E clock by two
|
|
input E_div,
|
|
// Next cycle would be raising/falling edge of E output
|
|
output E_PosClkEn, E_NegClkEn,
|
|
`endif
|
|
|
|
output FC0, output FC1, output FC2,
|
|
output BGn,
|
|
output oRESETn, output oHALTEDn,
|
|
input DTACKn, input VPAn,
|
|
input BERRn,
|
|
input BRn, BGACKn,
|
|
input IPL0n, input IPL1n, input IPL2n,
|
|
input [15:0] iEdb, output [15:0] oEdb,
|
|
output [23:1] eab
|
|
);
|
|
|
|
// wire clock = Clks.clk;
|
|
s_clks Clks;
|
|
|
|
assign Clks.clk = clk;
|
|
assign Clks.extReset = extReset;
|
|
assign Clks.pwrUp = pwrUp;
|
|
assign Clks.enPhi1 = enPhi1;
|
|
assign Clks.enPhi2 = enPhi2;
|
|
|
|
wire wClk;
|
|
|
|
// Internal sub clocks T1-T4
|
|
enum int unsigned { T0 = 0, T1, T2, T3, T4} tState;
|
|
wire enT1 = Clks.enPhi1 & (tState == T4) & ~wClk;
|
|
wire enT2 = Clks.enPhi2 & (tState == T1);
|
|
wire enT3 = Clks.enPhi1 & (tState == T2);
|
|
wire enT4 = Clks.enPhi2 & ((tState == T0) | (tState == T3));
|
|
|
|
// T4 continues ticking during reset and group0 exception.
|
|
// We also need it to erase ucode output latched on T4.
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp)
|
|
tState <= T0;
|
|
else begin
|
|
case( tState)
|
|
T0: if( Clks.enPhi2) tState <= T4;
|
|
T1: if( Clks.enPhi2) tState <= T2;
|
|
T2: if( Clks.enPhi1) tState <= T3;
|
|
T3: if( Clks.enPhi2) tState <= T4;
|
|
T4: if( Clks.enPhi1) tState <= wClk ? T0 : T1;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
// The following signals are synchronized with 3 couplers, phi1-phi2-phi1.
|
|
// Will be valid internally one cycle later if changed at the rasing edge of the clock.
|
|
//
|
|
// DTACK, BERR
|
|
|
|
// DTACK valid at S6 if changed at the rasing edge of S4 to avoid wait states.
|
|
// SNC (sncClkEn) is deasserted together (unless DTACK asserted too early).
|
|
//
|
|
// We synchronize some signals half clock earlier. We compensate later
|
|
reg rDtack, rBerr;
|
|
reg [2:0] rIpl, iIpl;
|
|
reg Vpai, BeI, Halti, BRi, BgackI, BeiDelay;
|
|
// reg rBR, rHALT;
|
|
wire BeDebounced = ~( BeI | BeiDelay);
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp) begin
|
|
rBerr <= 1'b0;
|
|
BeI <= 1'b0;
|
|
end
|
|
else if( Clks.enPhi2) begin
|
|
rDtack <= DTACKn;
|
|
rBerr <= BERRn;
|
|
rIpl <= ~{ IPL2n, IPL1n, IPL0n};
|
|
iIpl <= rIpl;
|
|
|
|
// Needed for cycle accuracy but only if BR or HALT are asserted on the wrong edge of the clock
|
|
// rBR <= BRn;
|
|
// rHALT <= HALTn;
|
|
end
|
|
else if( Clks.enPhi1) begin
|
|
Vpai <= VPAn;
|
|
BeI <= rBerr;
|
|
BeiDelay <= BeI;
|
|
BgackI <= BGACKn;
|
|
|
|
BRi <= BRn;
|
|
Halti <= HALTn;
|
|
// BRi <= rBR;
|
|
// Halti <= rHALT;
|
|
end
|
|
end
|
|
|
|
// Instantiate micro and nano rom
|
|
logic [NANO_WIDTH-1:0] nanoLatch;
|
|
logic [NANO_WIDTH-1:0] nanoOutput;
|
|
logic [UROM_WIDTH-1:0] microLatch;
|
|
logic [UROM_WIDTH-1:0] microOutput;
|
|
|
|
logic [UADDR_WIDTH-1:0] microAddr, nma;
|
|
logic [NADDR_WIDTH-1:0] nanoAddr, orgAddr;
|
|
wire rstUrom;
|
|
|
|
// For the time being, address translation is done for nanorom only.
|
|
microToNanoAddr microToNanoAddr( .uAddr( nma), .orgAddr);
|
|
|
|
// Output of these modules will be updated at T2 at the latest (depending on clock division)
|
|
|
|
nanoRom nanoRom( .clk( Clks.clk), .nanoAddr, .nanoOutput);
|
|
uRom uRom( .clk( Clks.clk), .microAddr, .microOutput);
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
// uaddr originally latched on T1, except bits 6 & 7, the conditional bits, on T2
|
|
// Seems we can latch whole address at either T1 or T2
|
|
|
|
// Originally it's invalid on hardware reset, and forced later when coming out of reset
|
|
if( Clks.pwrUp) begin
|
|
microAddr <= RSTP0_NMA;
|
|
nanoAddr <= RSTP0_NMA;
|
|
end
|
|
else if( enT1) begin
|
|
microAddr <= nma;
|
|
nanoAddr <= orgAddr; // Register translated uaddr to naddr
|
|
end
|
|
|
|
if( Clks.extReset) begin
|
|
microLatch <= '0;
|
|
nanoLatch <= '0;
|
|
end
|
|
else if( rstUrom) begin
|
|
// Originally reset these bits only. Not strictly needed like this.
|
|
// Can reset the whole register if it is important.
|
|
{ microLatch[16], microLatch[15], microLatch[0]} <= '0;
|
|
nanoLatch <= '0;
|
|
end
|
|
else if( enT3) begin
|
|
microLatch <= microOutput;
|
|
nanoLatch <= nanoOutput;
|
|
end
|
|
|
|
end
|
|
|
|
|
|
// Decoded nanocode signals
|
|
s_nanod Nanod;
|
|
// IRD decoded control signals
|
|
s_irdecod Irdecod;
|
|
|
|
//
|
|
reg Tpend;
|
|
reg intPend; // Interrupt pending
|
|
reg pswT, pswS;
|
|
reg [ 2:0] pswI;
|
|
wire [7:0] ccr;
|
|
|
|
wire [15:0] psw = { pswT, 1'b0, pswS, 2'b00, pswI, ccr};
|
|
|
|
reg [15:0] ftu;
|
|
reg [15:0] Irc, Ir, Ird;
|
|
|
|
wire [15:0] alue;
|
|
wire [15:0] Abl;
|
|
wire prenEmpty, au05z, dcr4, ze;
|
|
|
|
wire [UADDR_WIDTH-1:0] a1, a2, a3;
|
|
wire isPriv, isIllegal, isLineA, isLineF;
|
|
|
|
|
|
// IR & IRD forwarding
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT1) begin
|
|
if( Nanod.Ir2Ird)
|
|
Ird <= Ir;
|
|
else if(microLatch[0]) // prevented by IR => IRD !
|
|
Ir <= Irc;
|
|
end
|
|
end
|
|
|
|
wire [3:0] tvn;
|
|
wire waitBusCycle, busStarting;
|
|
wire BusRetry = 1'b0;
|
|
wire busAddrErr;
|
|
wire bciWrite; // Last bus cycle was write
|
|
wire bgBlock, busAvail;
|
|
wire addrOe;
|
|
|
|
wire busIsByte = Nanod.busByte & (Irdecod.isByte | Irdecod.isMovep);
|
|
wire aob0;
|
|
|
|
reg iStop; // Internal signal for ending bus cycle
|
|
reg A0Err; // Force bus/address error ucode
|
|
reg excRst; // Signal reset exception to sequencer
|
|
reg BerrA;
|
|
reg Spuria, Avia;
|
|
wire Iac;
|
|
|
|
reg rAddrErr, iBusErr, Err6591;
|
|
wire iAddrErr = rAddrErr & addrOe; // To simulate async reset
|
|
wire enErrClk;
|
|
|
|
// Reset micro/nano latch after T4 of the current ublock.
|
|
assign rstUrom = Clks.enPhi1 & enErrClk;
|
|
|
|
uaddrDecode uaddrDecode( .opcode( Ir), .a1, .a2, .a3, .isPriv, .isIllegal, .isLineA, .isLineF, .lineBmap());
|
|
|
|
sequencer sequencer( .Clks, .enT3, .microLatch, .Ird,
|
|
.A0Err, .excRst, .BerrA, .busAddrErr, .Spuria, .Avia,
|
|
.Tpend, .intPend, .isIllegal, .isPriv, .isLineA, .isLineF,
|
|
.nma, .a1, .a2, .a3, .tvn,
|
|
.psw, .prenEmpty, .au05z, .dcr4, .ze, .alue01( alue[1:0]), .i11( Irc[ 11]) );
|
|
|
|
excUnit excUnit( .Clks, .Nanod, .Irdecod, .enT1, .enT2, .enT3, .enT4,
|
|
.Ird, .ftu, .iEdb, .pswS,
|
|
.prenEmpty, .au05z, .dcr4, .ze, .AblOut( Abl), .eab, .aob0, .Irc, .oEdb,
|
|
.alue, .ccr);
|
|
|
|
nDecoder3 nDecoder( .Clks, .Nanod, .Irdecod, .enT2, .enT4, .microLatch, .nanoLatch);
|
|
|
|
irdDecode irdDecode( .ird( Ird), .Irdecod);
|
|
|
|
busControl busControl( .Clks, .enT1, .enT4, .permStart( Nanod.permStart), .permStop( Nanod.waitBusFinish), .iStop,
|
|
.aob0, .isWrite( Nanod.isWrite), .isRmc( Nanod.isRmc), .isByte( busIsByte), .busAvail,
|
|
.bciWrite, .addrOe, .bgBlock, .waitBusCycle, .busStarting, .busAddrErr,
|
|
.rDtack, .BeDebounced, .Vpai,
|
|
.ASn, .LDSn, .UDSn, .eRWn);
|
|
|
|
busArbiter busArbiter( .Clks, .BRi, .BgackI, .Halti, .bgBlock, .busAvail, .BGn);
|
|
|
|
|
|
// Output reset & halt control
|
|
wire [1:0] uFc = microLatch[ 16:15];
|
|
logic oReset, oHalted;
|
|
assign oRESETn = !oReset;
|
|
assign oHALTEDn = !oHalted;
|
|
|
|
// FC without permStart is special, either reset or halt
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp) begin
|
|
oReset <= 1'b0;
|
|
oHalted <= 1'b0;
|
|
end
|
|
else if( enT1) begin
|
|
oReset <= (uFc == 2'b01) & !Nanod.permStart;
|
|
oHalted <= (uFc == 2'b10) & !Nanod.permStart;
|
|
end
|
|
end
|
|
|
|
logic [2:0] rFC;
|
|
assign { FC2, FC1, FC0} = rFC; // ~rFC;
|
|
assign Iac = {rFC == 3'b111}; // & Control output enable !!
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset)
|
|
rFC <= '0;
|
|
else if( enT1 & Nanod.permStart) begin // S0 phase of bus cycle
|
|
rFC[2] <= pswS;
|
|
// If FC is type 'n' (0) at ucode, access type depends on PC relative mode
|
|
// We don't care about RZ in this case. Those uinstructions with RZ don't start a bus cycle.
|
|
rFC[1] <= microLatch[ 16] | ( ~microLatch[ 15] & Irdecod.isPcRel);
|
|
rFC[0] <= microLatch[ 15] | ( ~microLatch[ 16] & ~Irdecod.isPcRel);
|
|
end
|
|
end
|
|
|
|
|
|
// IPL interface
|
|
reg [2:0] inl; // Int level latch
|
|
reg updIll;
|
|
reg prevNmi;
|
|
|
|
wire nmi = (iIpl == 3'b111);
|
|
wire iplStable = (iIpl == rIpl);
|
|
wire iplComp = iIpl > pswI;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset) begin
|
|
intPend <= 1'b0;
|
|
prevNmi <= 1'b0;
|
|
end
|
|
else begin
|
|
if( Clks.enPhi2)
|
|
prevNmi <= nmi;
|
|
|
|
// Originally async RS-Latch on PHI2, followed by a transparent latch on T2
|
|
// Tricky because they might change simultaneously
|
|
// Syncronous on PHI2 is equivalent as long as the output is read on T3!
|
|
|
|
// Set on stable & NMI edge or compare
|
|
// Clear on: NMI Iack or (stable & !NMI & !Compare)
|
|
|
|
if( Clks.enPhi2) begin
|
|
if( iplStable & ((nmi & ~prevNmi) | iplComp) )
|
|
intPend <= 1'b1;
|
|
else if( ((inl == 3'b111) & Iac) | (iplStable & !nmi & !iplComp) )
|
|
intPend <= 1'b0;
|
|
end
|
|
end
|
|
|
|
if( Clks.extReset) begin
|
|
inl <= '1;
|
|
updIll <= 1'b0;
|
|
end
|
|
else if( enT4)
|
|
updIll <= microLatch[0]; // Update on any IRC->IR
|
|
else if( enT1 & updIll)
|
|
inl <= iIpl; // Timing is correct.
|
|
|
|
// Spurious interrupt, BERR on Interrupt Ack.
|
|
// Autovector interrupt. VPA on IACK.
|
|
// Timing is tight. Spuria is deasserted just after exception exception is recorded.
|
|
if( enT4) begin
|
|
Spuria <= ~BeiDelay & Iac;
|
|
Avia <= ~Vpai & Iac;
|
|
end
|
|
|
|
end
|
|
|
|
assign enErrClk = iAddrErr | iBusErr;
|
|
assign wClk = waitBusCycle | ~BeI | iAddrErr | Err6591;
|
|
|
|
// E clock and counter, VMA
|
|
reg [3:0] eCntr;
|
|
reg rVma;
|
|
|
|
assign VMAn = rVma;
|
|
|
|
// Internal stop just one cycle before E falling edge
|
|
wire xVma = ~rVma & (eCntr == 8) & en_E;
|
|
|
|
`ifdef USE_E_CLKEN
|
|
assign E_PosClkEn = (Clks.enPhi2 & (eCntr == 5) & en_E);
|
|
assign E_NegClkEn = (Clks.enPhi2 & (eCntr == 9) & en_E);
|
|
`endif
|
|
|
|
reg en_E;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp) begin
|
|
E <= 1'b0;
|
|
eCntr <='0;
|
|
rVma <= 1'b1;
|
|
en_E <= 1;
|
|
end
|
|
if( Clks.enPhi1)
|
|
if (E_div) en_E <= !en_E; else en_E <= 1'b1;
|
|
|
|
if( Clks.enPhi2 & en_E) begin
|
|
if( eCntr == 9)
|
|
E <= 1'b0;
|
|
else if( eCntr == 5)
|
|
E <= 1'b1;
|
|
|
|
if( eCntr == 9)
|
|
eCntr <= '0;
|
|
else
|
|
eCntr <= eCntr + 1'b1;
|
|
end
|
|
|
|
if( Clks.enPhi2 & addrOe & ~Vpai & (eCntr == 3) & en_E)
|
|
rVma <= 1'b0;
|
|
else if( Clks.enPhi1 & eCntr == '0 & en_E)
|
|
rVma <= 1'b1;
|
|
end
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
|
|
// This timing is critical to stop the clock phases at the exact point on bus/addr error.
|
|
// Timing should be such that current ublock completes (up to T3 or T4).
|
|
// But T1 for the next ublock shouldn't happen. Next T1 only after resetting ucode and ncode latches.
|
|
|
|
if( Clks.extReset)
|
|
rAddrErr <= 1'b0;
|
|
else if( Clks.enPhi1) begin
|
|
if( busAddrErr & addrOe) // Not on T1 ?!
|
|
rAddrErr <= 1'b1;
|
|
else if( ~addrOe) // Actually async reset!
|
|
rAddrErr <= 1'b0;
|
|
end
|
|
|
|
if( Clks.extReset)
|
|
iBusErr <= 1'b0;
|
|
else if( Clks.enPhi1) begin
|
|
iBusErr <= ( BerrA & ~BeI & ~Iac & !BusRetry);
|
|
end
|
|
|
|
if( Clks.extReset)
|
|
BerrA <= 1'b0;
|
|
else if( Clks.enPhi2) begin
|
|
if( ~BeI & ~Iac & addrOe)
|
|
BerrA <= 1'b1;
|
|
// else if( BeI & addrOe) // Bad, async reset since addrOe raising edge
|
|
else if( BeI & busStarting) // So replaced with this that raises one cycle earlier
|
|
BerrA <= 1'b0;
|
|
end
|
|
|
|
// Signal reset exception to sequencer.
|
|
// Originally cleared on 1st T2 after permstart. Must keep it until TVN latched.
|
|
if( Clks.extReset)
|
|
excRst <= 1'b1;
|
|
else if( enT2 & Nanod.permStart)
|
|
excRst <= 1'b0;
|
|
|
|
if( Clks.extReset)
|
|
A0Err <= 1'b1; // A0 Reset
|
|
else if( enT3) // Keep set until new urom words are being latched
|
|
A0Err <= 1'b0;
|
|
else if( Clks.enPhi1 & enErrClk & (busAddrErr | BerrA)) // Check bus error timing
|
|
A0Err <= 1'b1;
|
|
|
|
if( Clks.extReset) begin
|
|
iStop <= 1'b0;
|
|
Err6591 <= 1'b0;
|
|
end
|
|
else if( Clks.enPhi1)
|
|
Err6591 <= enErrClk;
|
|
else if( Clks.enPhi2)
|
|
iStop <= xVma | (Vpai & (iAddrErr | ~rBerr));
|
|
end
|
|
|
|
// PSW
|
|
logic irdToCcr_t4;
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp) begin
|
|
Tpend <= 1'b0;
|
|
{pswT, pswS, pswI } <= '0;
|
|
irdToCcr_t4 <= '0;
|
|
end
|
|
|
|
else if( enT4) begin
|
|
irdToCcr_t4 <= Irdecod.toCcr;
|
|
end
|
|
|
|
else if( enT3) begin
|
|
|
|
// UNIQUE IF !!
|
|
if( Nanod.updTpend)
|
|
Tpend <= pswT;
|
|
else if( Nanod.clrTpend)
|
|
Tpend <= 1'b0;
|
|
|
|
// UNIQUE IF !!
|
|
if( Nanod.ftu2Sr & !irdToCcr_t4)
|
|
{pswT, pswS, pswI } <= { ftu[ 15], ftu[13], ftu[10:8]};
|
|
else begin
|
|
if( Nanod.initST) begin
|
|
pswS <= 1'b1;
|
|
pswT <= 1'b0;
|
|
end
|
|
if( Nanod.inl2psw)
|
|
pswI <= inl;
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
// FTU
|
|
reg [4:0] ssw;
|
|
reg [3:0] tvnLatch;
|
|
logic [15:0] tvnMux;
|
|
reg inExcept01;
|
|
|
|
// Seems CPU has a buglet here.
|
|
// Flagging group 0 exceptions from TVN might not work because some bus cycles happen before TVN is updated.
|
|
// But doesn't matter because a group 0 exception inside another one will halt the CPU anyway and won't save the SSW.
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
|
|
// Updated at the start of the exception ucode
|
|
if( Nanod.updSsw & enT3) begin
|
|
ssw <= { ~bciWrite, inExcept01, rFC};
|
|
end
|
|
|
|
// Update TVN on T1 & IR=>IRD
|
|
if( enT1 & Nanod.Ir2Ird) begin
|
|
tvnLatch <= tvn;
|
|
inExcept01 <= (tvn != 1);
|
|
end
|
|
|
|
if( Clks.pwrUp)
|
|
ftu <= '0;
|
|
else if( enT3) begin
|
|
unique case( 1'b1)
|
|
Nanod.tvn2Ftu: ftu <= tvnMux;
|
|
|
|
// 0 on unused bits seem to come from ftuConst PLA previously clearing FBUS
|
|
Nanod.sr2Ftu: ftu <= {pswT, 1'b0, pswS, 2'b00, pswI, 3'b000, ccr[4:0] };
|
|
|
|
Nanod.ird2Ftu: ftu <= Ird;
|
|
Nanod.ssw2Ftu: ftu[4:0] <= ssw; // Undoc. Other bits must be preserved from IRD saved above!
|
|
Nanod.pswIToFtu: ftu <= { 12'hFFF, pswI, 1'b0}; // Interrupt level shifted
|
|
Nanod.const2Ftu: ftu <= Irdecod.ftuConst;
|
|
Nanod.abl2Pren: ftu <= Abl; // From ALU or datareg. Used for SR modify
|
|
default: ftu <= ftu;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
if( inExcept01) begin
|
|
// Unique IF !!!
|
|
if( tvnLatch == TVN_SPURIOUS)
|
|
tvnMux = {9'b0, 5'd24, 2'b00};
|
|
else if( tvnLatch == TVN_AUTOVEC)
|
|
tvnMux = {9'b0, 2'b11, pswI, 2'b00}; // Set TVN PLA decoder
|
|
else if( tvnLatch == TVN_INTERRUPT)
|
|
tvnMux = {6'b0, Ird[7:0], 2'b00}; // Interrupt vector was read and transferred to IRD
|
|
else
|
|
tvnMux = {10'b0, tvnLatch, 2'b00};
|
|
end
|
|
else
|
|
tvnMux = { 8'h0, Irdecod.macroTvn, 2'b00};
|
|
end
|
|
|
|
endmodule
|
|
|
|
// Nanorom (plus) decoder for die nanocode
|
|
module nDecoder3( input s_clks Clks, input s_irdecod Irdecod, output s_nanod Nanod,
|
|
input enT2, enT4,
|
|
input [UROM_WIDTH-1:0] microLatch,
|
|
input [NANO_WIDTH-1:0] nanoLatch);
|
|
|
|
localparam NANO_IR2IRD = 67;
|
|
localparam NANO_TOIRC = 66;
|
|
localparam NANO_ALU_COL = 63; // ALU operator column order is 63-64-65 !
|
|
localparam NANO_ALU_FI = 61; // ALU finish-init 62-61
|
|
localparam NANO_TODBIN = 60;
|
|
localparam NANO_ALUE = 57; // 57-59 shared with DCR control
|
|
localparam NANO_DCR = 57; // 57-59 shared with ALUE control
|
|
localparam NANO_DOBCTRL_1 = 56; // Input to control and permwrite
|
|
localparam NANO_LOWBYTE = 55; // Used by MOVEP
|
|
localparam NANO_HIGHBYTE = 54;
|
|
localparam NANO_DOBCTRL_0 = 53; // Input to control and permwrite
|
|
localparam NANO_ALU_DCTRL = 51; // 52-51 databus input mux control
|
|
localparam NANO_ALU_ACTRL = 50; // addrbus input mux control
|
|
localparam NANO_DBD2ALUB = 49;
|
|
localparam NANO_ABD2ALUB = 48;
|
|
localparam NANO_DBIN2DBD = 47;
|
|
localparam NANO_DBIN2ABD = 46;
|
|
localparam NANO_ALU2ABD = 45;
|
|
localparam NANO_ALU2DBD = 44;
|
|
localparam NANO_RZ = 43;
|
|
localparam NANO_BUSBYTE = 42; // If *both* this set and instruction is byte sized, then bus cycle is byte sized.
|
|
localparam NANO_PCLABL = 41;
|
|
localparam NANO_RXL_DBL = 40; // Switches RXL/RYL on DBL/ABL buses
|
|
localparam NANO_PCLDBL = 39;
|
|
localparam NANO_ABDHRECHARGE = 38;
|
|
localparam NANO_REG2ABL = 37; // register to ABL
|
|
localparam NANO_ABL2REG = 36; // ABL to register
|
|
localparam NANO_ABLABD = 35;
|
|
localparam NANO_DBLDBD = 34;
|
|
localparam NANO_DBL2REG = 33; // DBL to register
|
|
localparam NANO_REG2DBL = 32; // register to DBL
|
|
localparam NANO_ATLCTRL = 29; // 31-29
|
|
localparam NANO_FTUCONTROL = 25;
|
|
localparam NANO_SSP = 24;
|
|
localparam NANO_RXH_DBH = 22; // Switches RXH/RYH on DBH/ABH buses
|
|
localparam NANO_AUOUT = 20; // 21-20
|
|
localparam NANO_AUCLKEN = 19;
|
|
localparam NANO_AUCTRL = 16; // 18-16
|
|
localparam NANO_DBLDBH = 15;
|
|
localparam NANO_ABLABH = 14;
|
|
localparam NANO_EXT_ABH = 13;
|
|
localparam NANO_EXT_DBH = 12;
|
|
localparam NANO_ATHCTRL = 9; // 11-9
|
|
localparam NANO_REG2ABH = 8; // register to ABH
|
|
localparam NANO_ABH2REG = 7; // ABH to register
|
|
localparam NANO_REG2DBH = 6; // register to DBH
|
|
localparam NANO_DBH2REG = 5; // DBH to register
|
|
localparam NANO_AOBCTRL = 3; // 4-3
|
|
localparam NANO_PCH = 0; // 1-0 PchDbh PchAbh
|
|
localparam NANO_NO_SP_ALGN = 0; // Same bits as above when both set
|
|
|
|
localparam NANO_FTU_UPDTPEND = 1; // Also loads FTU constant according to IRD !
|
|
localparam NANO_FTU_INIT_ST = 15; // Set S, clear T (but not TPEND)
|
|
localparam NANO_FTU_CLRTPEND = 14;
|
|
localparam NANO_FTU_TVN = 13;
|
|
localparam NANO_FTU_ABL2PREN = 12; // ABL => FTU & ABL => PREN. Both transfers enabled, but only one will be used depending on uroutine.
|
|
localparam NANO_FTU_SSW = 11;
|
|
localparam NANO_FTU_RSTPREN = 10;
|
|
localparam NANO_FTU_IRD = 9;
|
|
localparam NANO_FTU_2ABL = 8;
|
|
localparam NANO_FTU_RDSR = 7;
|
|
localparam NANO_FTU_INL = 6;
|
|
localparam NANO_FTU_PSWI = 5; // Read Int Mask into FTU
|
|
localparam NANO_FTU_DBL = 4;
|
|
localparam NANO_FTU_2SR = 2;
|
|
localparam NANO_FTU_CONST = 1;
|
|
|
|
reg [3:0] ftuCtrl;
|
|
|
|
logic [2:0] athCtrl, atlCtrl;
|
|
assign athCtrl = nanoLatch[ NANO_ATHCTRL+2: NANO_ATHCTRL];
|
|
assign atlCtrl = nanoLatch[ NANO_ATLCTRL+2: NANO_ATLCTRL];
|
|
wire [1:0] aobCtrl = nanoLatch[ NANO_AOBCTRL+1:NANO_AOBCTRL];
|
|
wire [1:0] dobCtrl = {nanoLatch[ NANO_DOBCTRL_1], nanoLatch[NANO_DOBCTRL_0]};
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT4) begin
|
|
// Reverse order!
|
|
ftuCtrl <= { nanoLatch[ NANO_FTUCONTROL+0], nanoLatch[ NANO_FTUCONTROL+1], nanoLatch[ NANO_FTUCONTROL+2], nanoLatch[ NANO_FTUCONTROL+3]} ;
|
|
|
|
Nanod.auClkEn <= !nanoLatch[ NANO_AUCLKEN];
|
|
Nanod.auCntrl <= nanoLatch[ NANO_AUCTRL+2 : NANO_AUCTRL+0];
|
|
Nanod.noSpAlign <= (nanoLatch[ NANO_NO_SP_ALGN + 1:NANO_NO_SP_ALGN] == 2'b11);
|
|
Nanod.extDbh <= nanoLatch[ NANO_EXT_DBH];
|
|
Nanod.extAbh <= nanoLatch[ NANO_EXT_ABH];
|
|
Nanod.todbin <= nanoLatch[ NANO_TODBIN];
|
|
Nanod.toIrc <= nanoLatch[ NANO_TOIRC];
|
|
|
|
// ablAbd is disabled on byte transfers (adbhCharge plus irdIsByte). Not sure the combination makes much sense.
|
|
// It happens in a few cases but I don't see anything enabled on abL (or abH) section anyway.
|
|
|
|
Nanod.ablAbd <= nanoLatch[ NANO_ABLABD];
|
|
Nanod.ablAbh <= nanoLatch[ NANO_ABLABH];
|
|
Nanod.dblDbd <= nanoLatch[ NANO_DBLDBD];
|
|
Nanod.dblDbh <= nanoLatch[ NANO_DBLDBH];
|
|
|
|
Nanod.dbl2Atl <= (atlCtrl == 3'b010);
|
|
Nanod.atl2Dbl <= (atlCtrl == 3'b011);
|
|
Nanod.abl2Atl <= (atlCtrl == 3'b100);
|
|
Nanod.atl2Abl <= (atlCtrl == 3'b101);
|
|
|
|
Nanod.aob2Ab <= (athCtrl == 3'b101); // Used on BSER1 only
|
|
|
|
Nanod.abh2Ath <= (athCtrl == 3'b001) | (athCtrl == 3'b101);
|
|
Nanod.dbh2Ath <= (athCtrl == 3'b100);
|
|
Nanod.ath2Dbh <= (athCtrl == 3'b110);
|
|
Nanod.ath2Abh <= (athCtrl == 3'b011);
|
|
|
|
Nanod.alu2Dbd <= nanoLatch[ NANO_ALU2DBD];
|
|
Nanod.alu2Abd <= nanoLatch[ NANO_ALU2ABD];
|
|
|
|
Nanod.abd2Dcr <= (nanoLatch[ NANO_DCR+1:NANO_DCR] == 2'b11);
|
|
Nanod.dcr2Dbd <= (nanoLatch[ NANO_DCR+2:NANO_DCR+1] == 2'b11);
|
|
Nanod.dbd2Alue <= (nanoLatch[ NANO_ALUE+2:NANO_ALUE+1] == 2'b10);
|
|
Nanod.alue2Dbd <= (nanoLatch[ NANO_ALUE+1:NANO_ALUE] == 2'b01);
|
|
|
|
Nanod.dbd2Alub <= nanoLatch[ NANO_DBD2ALUB];
|
|
Nanod.abd2Alub <= nanoLatch[ NANO_ABD2ALUB];
|
|
|
|
// Originally not latched. We better should because we transfer one cycle later, T3 instead of T1.
|
|
Nanod.dobCtrl <= dobCtrl;
|
|
// Nanod.adb2Dob <= (dobCtrl == 2'b10); Nanod.dbd2Dob <= (dobCtrl == 2'b01); Nanod.alu2Dob <= (dobCtrl == 2'b11);
|
|
|
|
end
|
|
end
|
|
|
|
// Update SSW at the start of Bus/Addr error ucode
|
|
assign Nanod.updSsw = Nanod.aob2Ab;
|
|
|
|
assign Nanod.updTpend = (ftuCtrl == NANO_FTU_UPDTPEND);
|
|
assign Nanod.clrTpend = (ftuCtrl == NANO_FTU_CLRTPEND);
|
|
assign Nanod.tvn2Ftu = (ftuCtrl == NANO_FTU_TVN);
|
|
assign Nanod.const2Ftu = (ftuCtrl == NANO_FTU_CONST);
|
|
assign Nanod.ftu2Dbl = (ftuCtrl == NANO_FTU_DBL) | ( ftuCtrl == NANO_FTU_INL);
|
|
assign Nanod.ftu2Abl = (ftuCtrl == NANO_FTU_2ABL);
|
|
assign Nanod.inl2psw = (ftuCtrl == NANO_FTU_INL);
|
|
assign Nanod.pswIToFtu = (ftuCtrl == NANO_FTU_PSWI);
|
|
assign Nanod.ftu2Sr = (ftuCtrl == NANO_FTU_2SR);
|
|
assign Nanod.sr2Ftu = (ftuCtrl == NANO_FTU_RDSR);
|
|
assign Nanod.ird2Ftu = (ftuCtrl == NANO_FTU_IRD); // Used on bus/addr error
|
|
assign Nanod.ssw2Ftu = (ftuCtrl == NANO_FTU_SSW);
|
|
assign Nanod.initST = (ftuCtrl == NANO_FTU_INL) | (ftuCtrl == NANO_FTU_CLRTPEND) | (ftuCtrl == NANO_FTU_INIT_ST);
|
|
assign Nanod.abl2Pren = (ftuCtrl == NANO_FTU_ABL2PREN);
|
|
assign Nanod.updPren = (ftuCtrl == NANO_FTU_RSTPREN);
|
|
|
|
assign Nanod.Ir2Ird = nanoLatch[ NANO_IR2IRD];
|
|
|
|
// ALU control better latched later after combining with IRD decoding
|
|
|
|
assign Nanod.aluDctrl = nanoLatch[ NANO_ALU_DCTRL+1 : NANO_ALU_DCTRL];
|
|
assign Nanod.aluActrl = nanoLatch[ NANO_ALU_ACTRL];
|
|
assign Nanod.aluColumn = { nanoLatch[ NANO_ALU_COL], nanoLatch[ NANO_ALU_COL+1], nanoLatch[ NANO_ALU_COL+2]};
|
|
wire [1:0] aluFinInit = nanoLatch[ NANO_ALU_FI+1:NANO_ALU_FI];
|
|
assign Nanod.aluFinish = (aluFinInit == 2'b10);
|
|
assign Nanod.aluInit = (aluFinInit == 2'b01);
|
|
|
|
// FTU 2 CCR encoded as both ALU Init and ALU Finish set.
|
|
// In theory this encoding allows writes to CCR without writing to SR
|
|
// But FTU 2 CCR and to SR are both set together at nanorom.
|
|
assign Nanod.ftu2Ccr = ( aluFinInit == 2'b11);
|
|
|
|
assign Nanod.abdIsByte = nanoLatch[ NANO_ABDHRECHARGE];
|
|
|
|
// Not being latched on T4 creates non unique case warning!
|
|
assign Nanod.au2Db = (nanoLatch[ NANO_AUOUT + 1: NANO_AUOUT] == 2'b01);
|
|
assign Nanod.au2Ab = (nanoLatch[ NANO_AUOUT + 1: NANO_AUOUT] == 2'b10);
|
|
assign Nanod.au2Pc = (nanoLatch[ NANO_AUOUT + 1: NANO_AUOUT] == 2'b11);
|
|
|
|
assign Nanod.db2Aob = (aobCtrl == 2'b10);
|
|
assign Nanod.ab2Aob = (aobCtrl == 2'b01);
|
|
assign Nanod.au2Aob = (aobCtrl == 2'b11);
|
|
|
|
assign Nanod.dbin2Abd = nanoLatch[ NANO_DBIN2ABD];
|
|
assign Nanod.dbin2Dbd = nanoLatch[ NANO_DBIN2DBD];
|
|
|
|
assign Nanod.permStart = (| aobCtrl);
|
|
assign Nanod.isWrite = ( | dobCtrl);
|
|
assign Nanod.waitBusFinish = nanoLatch[ NANO_TOIRC] | nanoLatch[ NANO_TODBIN] | Nanod.isWrite;
|
|
assign Nanod.busByte = nanoLatch[ NANO_BUSBYTE];
|
|
|
|
assign Nanod.noLowByte = nanoLatch[ NANO_LOWBYTE];
|
|
assign Nanod.noHighByte = nanoLatch[ NANO_HIGHBYTE];
|
|
|
|
// Not registered. Register at T4 after combining
|
|
// Might be better to remove all those and combine here instead of at execution unit !!
|
|
assign Nanod.abl2reg = nanoLatch[ NANO_ABL2REG];
|
|
assign Nanod.abh2reg = nanoLatch[ NANO_ABH2REG];
|
|
assign Nanod.dbl2reg = nanoLatch[ NANO_DBL2REG];
|
|
assign Nanod.dbh2reg = nanoLatch[ NANO_DBH2REG];
|
|
assign Nanod.reg2dbl = nanoLatch[ NANO_REG2DBL];
|
|
assign Nanod.reg2dbh = nanoLatch[ NANO_REG2DBH];
|
|
assign Nanod.reg2abl = nanoLatch[ NANO_REG2ABL];
|
|
assign Nanod.reg2abh = nanoLatch[ NANO_REG2ABH];
|
|
|
|
assign Nanod.ssp = nanoLatch[ NANO_SSP];
|
|
|
|
assign Nanod.rz = nanoLatch[ NANO_RZ];
|
|
|
|
// Actually DTL can't happen on PC relative mode. See IR decoder.
|
|
|
|
wire dtldbd = 1'b0;
|
|
wire dthdbh = 1'b0;
|
|
wire dtlabd = 1'b0;
|
|
wire dthabh = 1'b0;
|
|
|
|
wire dblSpecial = Nanod.pcldbl | dtldbd;
|
|
wire dbhSpecial = Nanod.pchdbh | dthdbh;
|
|
wire ablSpecial = Nanod.pclabl | dtlabd;
|
|
wire abhSpecial = Nanod.pchabh | dthabh;
|
|
|
|
//
|
|
// Combine with IRD decoding
|
|
// Careful that IRD is updated only on T1! All output depending on IRD must be latched on T4!
|
|
//
|
|
|
|
// PC used instead of RY on PC relative instuctions
|
|
|
|
assign Nanod.rxlDbl = nanoLatch[ NANO_RXL_DBL];
|
|
wire isPcRel = Irdecod.isPcRel & !Nanod.rz;
|
|
wire pcRelDbl = isPcRel & !nanoLatch[ NANO_RXL_DBL];
|
|
wire pcRelDbh = isPcRel & !nanoLatch[ NANO_RXH_DBH];
|
|
wire pcRelAbl = isPcRel & nanoLatch[ NANO_RXL_DBL];
|
|
wire pcRelAbh = isPcRel & nanoLatch[ NANO_RXH_DBH];
|
|
|
|
assign Nanod.pcldbl = nanoLatch[ NANO_PCLDBL] | pcRelDbl;
|
|
assign Nanod.pchdbh = (nanoLatch[ NANO_PCH+1:NANO_PCH] == 2'b01) | pcRelDbh;
|
|
|
|
assign Nanod.pclabl = nanoLatch[ NANO_PCLABL] | pcRelAbl;
|
|
assign Nanod.pchabh = (nanoLatch[ NANO_PCH+1:NANO_PCH] == 2'b10) | pcRelAbh;
|
|
|
|
// Might be better not to register these signals to allow latching RX/RY mux earlier!
|
|
// But then must latch Irdecod.isPcRel on T3!
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT4) begin
|
|
Nanod.rxl2db <= Nanod.reg2dbl & !dblSpecial & nanoLatch[ NANO_RXL_DBL];
|
|
Nanod.rxl2ab <= Nanod.reg2abl & !ablSpecial & !nanoLatch[ NANO_RXL_DBL];
|
|
|
|
Nanod.dbl2rxl <= Nanod.dbl2reg & !dblSpecial & nanoLatch[ NANO_RXL_DBL];
|
|
Nanod.abl2rxl <= Nanod.abl2reg & !ablSpecial & !nanoLatch[ NANO_RXL_DBL];
|
|
|
|
Nanod.rxh2dbh <= Nanod.reg2dbh & !dbhSpecial & nanoLatch[ NANO_RXH_DBH];
|
|
Nanod.rxh2abh <= Nanod.reg2abh & !abhSpecial & !nanoLatch[ NANO_RXH_DBH];
|
|
|
|
Nanod.dbh2rxh <= Nanod.dbh2reg & !dbhSpecial & nanoLatch[ NANO_RXH_DBH];
|
|
Nanod.abh2rxh <= Nanod.abh2reg & !abhSpecial & !nanoLatch[ NANO_RXH_DBH];
|
|
|
|
Nanod.dbh2ryh <= Nanod.dbh2reg & !dbhSpecial & !nanoLatch[ NANO_RXH_DBH];
|
|
Nanod.abh2ryh <= Nanod.abh2reg & !abhSpecial & nanoLatch[ NANO_RXH_DBH];
|
|
|
|
Nanod.dbl2ryl <= Nanod.dbl2reg & !dblSpecial & !nanoLatch[ NANO_RXL_DBL];
|
|
Nanod.abl2ryl <= Nanod.abl2reg & !ablSpecial & nanoLatch[ NANO_RXL_DBL];
|
|
|
|
Nanod.ryl2db <= Nanod.reg2dbl & !dblSpecial & !nanoLatch[ NANO_RXL_DBL];
|
|
Nanod.ryl2ab <= Nanod.reg2abl & !ablSpecial & nanoLatch[ NANO_RXL_DBL];
|
|
|
|
Nanod.ryh2dbh <= Nanod.reg2dbh & !dbhSpecial & !nanoLatch[ NANO_RXH_DBH];
|
|
Nanod.ryh2abh <= Nanod.reg2abh & !abhSpecial & nanoLatch[ NANO_RXH_DBH];
|
|
end
|
|
|
|
// Originally isTas only delayed on T2 (and seems only a late mask rev fix)
|
|
// Better latch the combination on T4
|
|
if( enT4)
|
|
Nanod.isRmc <= Irdecod.isTas & nanoLatch[ NANO_BUSBYTE];
|
|
end
|
|
|
|
|
|
endmodule
|
|
|
|
//
|
|
// IRD execution decoder. Complements nano code decoder
|
|
//
|
|
// IRD updated on T1, while ncode still executing. To avoid using the next IRD,
|
|
// decoded signals must be registered on T3, or T4 before using them.
|
|
//
|
|
module irdDecode( input [15:0] ird,
|
|
output s_irdecod Irdecod);
|
|
|
|
wire [3:0] line = ird[15:12];
|
|
logic [15:0] lineOnehot;
|
|
|
|
// This can be registered and pipelined from the IR decoder !
|
|
onehotEncoder4 irdLines( line, lineOnehot);
|
|
|
|
wire isRegShift = (lineOnehot['he]) & (ird[7:6] != 2'b11);
|
|
wire isDynShift = isRegShift & ird[5];
|
|
|
|
assign Irdecod.isPcRel = (& ird[ 5:3]) & ~isDynShift & !ird[2] & ird[1];
|
|
assign Irdecod.isTas = lineOnehot[4] & (ird[11:6] == 6'b101011);
|
|
|
|
assign Irdecod.rx = ird[11:9];
|
|
assign Irdecod.ry = ird[ 2:0];
|
|
|
|
wire isPreDecr = (ird[ 5:3] == 3'b100);
|
|
wire eaAreg = (ird[5:3] == 3'b001);
|
|
|
|
// rx is A or D
|
|
// movem
|
|
always_comb begin
|
|
unique case( 1'b1)
|
|
lineOnehot[1],
|
|
lineOnehot[2],
|
|
lineOnehot[3]:
|
|
// MOVE: RX always Areg except if dest mode is Dn 000
|
|
Irdecod.rxIsAreg = (| ird[8:6]);
|
|
|
|
lineOnehot[4]: Irdecod.rxIsAreg = (& ird[8:6]); // not CHK (LEA)
|
|
|
|
lineOnehot['h8]: Irdecod.rxIsAreg = eaAreg & ird[8] & ~ird[7]; // SBCD
|
|
lineOnehot['hc]: Irdecod.rxIsAreg = eaAreg & ird[8] & ~ird[7]; // ABCD/EXG An,An
|
|
|
|
lineOnehot['h9],
|
|
lineOnehot['hb],
|
|
lineOnehot['hd]: Irdecod.rxIsAreg =
|
|
(ird[7] & ird[6]) | // SUBA/CMPA/ADDA
|
|
(eaAreg & ird[8] & (ird[7:6] != 2'b11)); // SUBX/CMPM/ADDX
|
|
default:
|
|
Irdecod.rxIsAreg = Irdecod.implicitSp;
|
|
endcase
|
|
end
|
|
|
|
// RX is movem
|
|
always_comb begin
|
|
Irdecod.rxIsMovem = lineOnehot[4] & ~ird[8] & ~Irdecod.implicitSp;
|
|
end
|
|
assign Irdecod.movemPreDecr = Irdecod.rxIsMovem & isPreDecr;
|
|
|
|
// RX is DT.
|
|
// but SSP explicit or pc explicit has higher priority!
|
|
// addq/subq (scc & dbcc also, but don't use rx)
|
|
// Immediate including static bit
|
|
assign Irdecod.rxIsDt = lineOnehot[5] | (lineOnehot[0] & ~ird[8]);
|
|
|
|
// RX is USP
|
|
assign Irdecod.rxIsUsp = lineOnehot[4] & (ird[ 11:4] == 8'he6);
|
|
|
|
// RY is DT
|
|
// rz or PC explicit has higher priority
|
|
|
|
wire eaImmOrAbs = (ird[5:3] == 3'b111) & ~ird[1];
|
|
assign Irdecod.ryIsDt = eaImmOrAbs & ~isRegShift;
|
|
|
|
// RY is Address register
|
|
always_comb begin
|
|
logic eaIsAreg;
|
|
|
|
// On most cases RY is Areg expect if mode is 000 (DATA REG) or 111 (IMM, ABS,PC REL)
|
|
eaIsAreg = (ird[5:3] != 3'b000) & (ird[5:3] != 3'b111);
|
|
|
|
unique case( 1'b1)
|
|
// MOVE: RY always Areg expect if mode is 000 (DATA REG) or 111 (IMM, ABS,PC REL)
|
|
// Most lines, including misc line 4, also.
|
|
default: Irdecod.ryIsAreg = eaIsAreg;
|
|
|
|
lineOnehot[5]: // DBcc is an exception
|
|
Irdecod.ryIsAreg = eaIsAreg & (ird[7:3] != 5'b11001);
|
|
|
|
lineOnehot[6],
|
|
lineOnehot[7]: Irdecod.ryIsAreg = 1'b0;
|
|
|
|
lineOnehot['he]:
|
|
Irdecod.ryIsAreg = ~isRegShift;
|
|
endcase
|
|
end
|
|
|
|
// Byte sized instruction
|
|
|
|
// Original implementation sets this for some instructions that aren't really byte size
|
|
// but doesn't matter because they don't have a byte transfer enabled at nanocode, such as MOVEQ
|
|
|
|
wire xIsScc = (ird[7:6] == 2'b11) & (ird[5:3] != 3'b001);
|
|
wire xStaticMem = (ird[11:8] == 4'b1000) & (ird[5:4] == 2'b00); // Static bit to mem
|
|
always_comb begin
|
|
unique case( 1'b1)
|
|
lineOnehot[0]:
|
|
Irdecod.isByte =
|
|
( ird[8] & (ird[5:4] != 2'b00) ) | // Dynamic bit to mem
|
|
( (ird[11:8] == 4'b1000) & (ird[5:4] != 2'b00) ) | // Static bit to mem
|
|
( (ird[8:7] == 2'b10) & (ird[5:3] == 3'b001) ) | // Movep from mem only! For byte mux
|
|
( (ird[8:6] == 3'b000) & !xStaticMem ); // Immediate byte
|
|
|
|
lineOnehot[1]: Irdecod.isByte = 1'b1; // MOVE.B
|
|
|
|
|
|
lineOnehot[4]: Irdecod.isByte = (ird[7:6] == 2'b00) | Irdecod.isTas;
|
|
lineOnehot[5]: Irdecod.isByte = (ird[7:6] == 2'b00) | xIsScc;
|
|
|
|
lineOnehot[8],
|
|
lineOnehot[9],
|
|
lineOnehot['hb],
|
|
lineOnehot['hc],
|
|
lineOnehot['hd],
|
|
lineOnehot['he]: Irdecod.isByte = (ird[7:6] == 2'b00);
|
|
|
|
default: Irdecod.isByte = 1'b0;
|
|
endcase
|
|
end
|
|
|
|
// Need it for special byte size. Bus is byte, but whole register word is modified.
|
|
assign Irdecod.isMovep = lineOnehot[0] & ird[8] & eaAreg;
|
|
|
|
|
|
// rxIsSP implicit use of RX for actual SP transfer
|
|
//
|
|
// This logic is simple and will include some instructions that don't actually reference SP.
|
|
// But doesn't matter as long as they don't perform any RX transfer.
|
|
|
|
always_comb begin
|
|
unique case( 1'b1)
|
|
lineOnehot[6]: Irdecod.implicitSp = (ird[11:8] == 4'b0001); // BSR
|
|
lineOnehot[4]:
|
|
// Misc like RTS, JSR, etc
|
|
Irdecod.implicitSp = (ird[11:8] == 4'b1110) | (ird[11:6] == 6'b1000_01);
|
|
default: Irdecod.implicitSp = 1'b0;
|
|
endcase
|
|
end
|
|
|
|
// Modify CCR (and not SR)
|
|
// Probably overkill !! Only needs to distinguish SR vs CCR
|
|
// RTR, MOVE to CCR, xxxI to CCR
|
|
assign Irdecod.toCcr = ( lineOnehot[4] & ((ird[11:0] == 12'he77) | (ird[11:6] == 6'b010011)) ) |
|
|
( lineOnehot[0] & (ird[8:6] == 3'b000));
|
|
|
|
// FTU constants
|
|
// This should not be latched on T3/T4. Latch on T2 or not at all. FTU needs it on next T3.
|
|
// Note: Reset instruction gets constant from ALU not from FTU!
|
|
logic [15:0] ftuConst;
|
|
wire [3:0] zero28 = (ird[11:9] == 0) ? 4'h8 : { 1'b0, ird[11:9]}; // xltate 0,1-7 into 8,1-7
|
|
|
|
always_comb begin
|
|
unique case( 1'b1)
|
|
lineOnehot[6], // Bcc short
|
|
lineOnehot[7]: ftuConst = { { 8{ ird[ 7]}}, ird[ 7:0] }; // MOVEQ
|
|
|
|
lineOnehot['h5], // addq/subq/static shift double check this
|
|
lineOnehot['he]: ftuConst = { 12'b0, zero28};
|
|
|
|
// MULU/MULS DIVU/DIVS
|
|
lineOnehot['h8],
|
|
lineOnehot['hc]: ftuConst = 16'h0f;
|
|
|
|
lineOnehot[4]: ftuConst = 16'h80; // TAS
|
|
|
|
default: ftuConst = '0;
|
|
endcase
|
|
end
|
|
assign Irdecod.ftuConst = ftuConst;
|
|
|
|
//
|
|
// TRAP Vector # for group 2 exceptions
|
|
//
|
|
|
|
always_comb begin
|
|
if( lineOnehot[4]) begin
|
|
case ( ird[6:5])
|
|
2'b00,2'b01: Irdecod.macroTvn = 6; // CHK
|
|
2'b11: Irdecod.macroTvn = 7; // TRAPV
|
|
2'b10: Irdecod.macroTvn = {2'b10, ird[3:0]}; // TRAP
|
|
endcase
|
|
end
|
|
else
|
|
Irdecod.macroTvn = 5; // Division by zero
|
|
end
|
|
|
|
|
|
wire eaAdir = (ird[ 5:3] == 3'b001);
|
|
wire size11 = ird[7] & ird[6];
|
|
|
|
// Opcodes variants that don't affect flags
|
|
// ADDA/SUBA ADDQ/SUBQ MOVEA
|
|
|
|
assign Irdecod.inhibitCcr =
|
|
( (lineOnehot[9] | lineOnehot['hd]) & size11) | // ADDA/SUBA
|
|
( lineOnehot[5] & eaAdir) | // ADDQ/SUBQ to An (originally checks for line[4] as well !?)
|
|
( (lineOnehot[2] | lineOnehot[3]) & ird[8:6] == 3'b001); // MOVEA
|
|
|
|
endmodule
|
|
|
|
/*
|
|
Execution unit
|
|
|
|
Executes register transfers set by the microcode. Originally through a set of bidirectional buses.
|
|
Most sources are available at T3, but DBIN only at T4! CCR also might be updated at T4, but it is not connected to these buses.
|
|
We mux at T1 and T2, then transfer to the destination at T3. The exception is AOB that need to be updated earlier.
|
|
|
|
*/
|
|
|
|
module excUnit( input s_clks Clks,
|
|
input enT1, enT2, enT3, enT4,
|
|
input s_nanod Nanod, input s_irdecod Irdecod,
|
|
input [15:0] Ird, // ALU row (and others) decoder needs it
|
|
input pswS,
|
|
input [15:0] ftu,
|
|
input [15:0] iEdb,
|
|
|
|
output logic [7:0] ccr,
|
|
output [15:0] alue,
|
|
|
|
output prenEmpty, au05z,
|
|
output logic dcr4, ze,
|
|
output logic aob0,
|
|
output [15:0] AblOut,
|
|
output logic [15:0] Irc,
|
|
output logic [15:0] oEdb,
|
|
output logic [23:1] eab);
|
|
|
|
localparam REG_USP = 15;
|
|
localparam REG_SSP = 16;
|
|
localparam REG_DT = 17;
|
|
|
|
// Register file
|
|
reg [15:0] regs68L[ 18];
|
|
reg [15:0] regs68H[ 18];
|
|
|
|
// synthesis translate off
|
|
/*
|
|
It is bad practice to initialize simulation registers that the hardware doesn't.
|
|
There is risk that simulation would be different than the real hardware. But in this case is the other way around.
|
|
Some ROM uses something like sub.l An,An at powerup which clears the register
|
|
Simulator power ups the registers with 'X, as they are really undetermined at the real hardware.
|
|
But the simulator doesn't realize (it can't) that the same value is substracting from itself,
|
|
and that the result should be zero even when it's 'X - 'X.
|
|
*/
|
|
|
|
initial begin
|
|
for( int i = 0; i < 18; i++) begin
|
|
regs68L[i] <= '0;
|
|
regs68H[i] <= '0;
|
|
end
|
|
end
|
|
|
|
// For simulation display only
|
|
wire [31:0] SSP = { regs68H[REG_SSP], regs68L[REG_SSP]};
|
|
|
|
// synthesis translate on
|
|
|
|
|
|
wire [15:0] aluOut;
|
|
wire [15:0] dbin;
|
|
logic [15:0] dcrOutput;
|
|
|
|
reg [15:0] PcL, PcH;
|
|
|
|
reg [31:0] auReg, aob;
|
|
|
|
reg [15:0] Ath, Atl;
|
|
|
|
// Bus execution
|
|
reg [15:0] Dbl, Dbh;
|
|
reg [15:0] Abh, Abl;
|
|
reg [15:0] Abd, Dbd;
|
|
|
|
assign AblOut = Abl;
|
|
assign au05z = (~| auReg[5:0]);
|
|
|
|
logic [15:0] dblMux, dbhMux;
|
|
logic [15:0] abhMux, ablMux;
|
|
logic [15:0] abdMux, dbdMux;
|
|
|
|
logic abdIsByte;
|
|
|
|
logic Pcl2Dbl, Pch2Dbh;
|
|
logic Pcl2Abl, Pch2Abh;
|
|
|
|
|
|
// RX RY muxes
|
|
// RX and RY actual registers
|
|
logic [4:0] actualRx, actualRy;
|
|
logic [3:0] movemRx;
|
|
logic byteNotSpAlign; // Byte instruction and no sp word align
|
|
|
|
// IRD decoded signals must be latched. See comments on decoder
|
|
// But nanostore decoding can't be latched before T4.
|
|
//
|
|
// If we need this earlier we can register IRD decode on T3 and use nano async
|
|
|
|
logic [4:0] rxMux, ryMux;
|
|
logic [3:0] rxReg, ryReg;
|
|
logic rxIsSp, ryIsSp;
|
|
logic rxIsAreg, ryIsAreg;
|
|
|
|
always_comb begin
|
|
|
|
// Unique IF !!
|
|
if( Nanod.ssp) begin
|
|
rxMux = REG_SSP;
|
|
rxIsSp = 1'b1;
|
|
rxReg = 1'bX;
|
|
end
|
|
else if( Irdecod.rxIsUsp) begin
|
|
rxMux = REG_USP;
|
|
rxIsSp = 1'b1;
|
|
rxReg = 1'bX;
|
|
end
|
|
else if( Irdecod.rxIsDt & !Irdecod.implicitSp) begin
|
|
rxMux = REG_DT;
|
|
rxIsSp = 1'b0;
|
|
rxReg = 1'bX;
|
|
end
|
|
else begin
|
|
if( Irdecod.implicitSp)
|
|
rxReg = 15;
|
|
else if( Irdecod.rxIsMovem)
|
|
rxReg = movemRx;
|
|
else
|
|
rxReg = { Irdecod.rxIsAreg, Irdecod.rx};
|
|
|
|
if( (& rxReg)) begin
|
|
rxMux = pswS ? REG_SSP : 15;
|
|
rxIsSp = 1'b1;
|
|
end
|
|
else begin
|
|
rxMux = { 1'b0, rxReg};
|
|
rxIsSp = 1'b0;
|
|
end
|
|
end
|
|
|
|
// RZ has higher priority!
|
|
if( Irdecod.ryIsDt & !Nanod.rz) begin
|
|
ryMux = REG_DT;
|
|
ryIsSp = 1'b0;
|
|
ryReg = 'X;
|
|
end
|
|
else begin
|
|
ryReg = Nanod.rz ? Irc[15:12] : {Irdecod.ryIsAreg, Irdecod.ry};
|
|
ryIsSp = (& ryReg);
|
|
if( ryIsSp & pswS) // No implicit SP on RY
|
|
ryMux = REG_SSP;
|
|
else
|
|
ryMux = { 1'b0, ryReg};
|
|
end
|
|
|
|
end
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT4) begin
|
|
byteNotSpAlign <= Irdecod.isByte & ~(Nanod.rxlDbl ? rxIsSp : ryIsSp);
|
|
|
|
actualRx <= rxMux;
|
|
actualRy <= ryMux;
|
|
|
|
rxIsAreg <= rxIsSp | rxMux[3];
|
|
ryIsAreg <= ryIsSp | ryMux[3];
|
|
end
|
|
|
|
if( enT4)
|
|
abdIsByte <= Nanod.abdIsByte & Irdecod.isByte;
|
|
end
|
|
|
|
// Set RX/RY low word to which bus segment is connected.
|
|
|
|
wire ryl2Abl = Nanod.ryl2ab & (ryIsAreg | Nanod.ablAbd);
|
|
wire ryl2Abd = Nanod.ryl2ab & (~ryIsAreg | Nanod.ablAbd);
|
|
wire ryl2Dbl = Nanod.ryl2db & (ryIsAreg | Nanod.dblDbd);
|
|
wire ryl2Dbd = Nanod.ryl2db & (~ryIsAreg | Nanod.dblDbd);
|
|
|
|
wire rxl2Abl = Nanod.rxl2ab & (rxIsAreg | Nanod.ablAbd);
|
|
wire rxl2Abd = Nanod.rxl2ab & (~rxIsAreg | Nanod.ablAbd);
|
|
wire rxl2Dbl = Nanod.rxl2db & (rxIsAreg | Nanod.dblDbd);
|
|
wire rxl2Dbd = Nanod.rxl2db & (~rxIsAreg | Nanod.dblDbd);
|
|
|
|
// Buses. Main mux
|
|
|
|
logic abhIdle, ablIdle, abdIdle;
|
|
logic dbhIdle, dblIdle, dbdIdle;
|
|
|
|
always_comb begin
|
|
{abhIdle, ablIdle, abdIdle} = '0;
|
|
{dbhIdle, dblIdle, dbdIdle} = '0;
|
|
|
|
unique case( 1'b1)
|
|
ryl2Dbd: dbdMux = regs68L[ actualRy];
|
|
rxl2Dbd: dbdMux = regs68L[ actualRx];
|
|
Nanod.alue2Dbd: dbdMux = alue;
|
|
Nanod.dbin2Dbd: dbdMux = dbin;
|
|
Nanod.alu2Dbd: dbdMux = aluOut;
|
|
Nanod.dcr2Dbd: dbdMux = dcrOutput;
|
|
default: begin dbdMux = 'X; dbdIdle = 1'b1; end
|
|
endcase
|
|
|
|
unique case( 1'b1)
|
|
rxl2Dbl: dblMux = regs68L[ actualRx];
|
|
ryl2Dbl: dblMux = regs68L[ actualRy];
|
|
Nanod.ftu2Dbl: dblMux = ftu;
|
|
Nanod.au2Db: dblMux = auReg[15:0];
|
|
Nanod.atl2Dbl: dblMux = Atl;
|
|
Pcl2Dbl: dblMux = PcL;
|
|
default: begin dblMux = 'X; dblIdle = 1'b1; end
|
|
endcase
|
|
|
|
unique case( 1'b1)
|
|
Nanod.rxh2dbh: dbhMux = regs68H[ actualRx];
|
|
Nanod.ryh2dbh: dbhMux = regs68H[ actualRy];
|
|
Nanod.au2Db: dbhMux = auReg[31:16];
|
|
Nanod.ath2Dbh: dbhMux = Ath;
|
|
Pch2Dbh: dbhMux = PcH;
|
|
default: begin dbhMux = 'X; dbhIdle = 1'b1; end
|
|
endcase
|
|
|
|
unique case( 1'b1)
|
|
ryl2Abd: abdMux = regs68L[ actualRy];
|
|
rxl2Abd: abdMux = regs68L[ actualRx];
|
|
Nanod.dbin2Abd: abdMux = dbin;
|
|
Nanod.alu2Abd: abdMux = aluOut;
|
|
default: begin abdMux = 'X; abdIdle = 1'b1; end
|
|
endcase
|
|
|
|
unique case( 1'b1)
|
|
Pcl2Abl: ablMux = PcL;
|
|
rxl2Abl: ablMux = regs68L[ actualRx];
|
|
ryl2Abl: ablMux = regs68L[ actualRy];
|
|
Nanod.ftu2Abl: ablMux = ftu;
|
|
Nanod.au2Ab: ablMux = auReg[15:0];
|
|
Nanod.aob2Ab: ablMux = aob[15:0];
|
|
Nanod.atl2Abl: ablMux = Atl;
|
|
default: begin ablMux = 'X; ablIdle = 1'b1; end
|
|
endcase
|
|
|
|
unique case( 1'b1)
|
|
Pch2Abh: abhMux = PcH;
|
|
Nanod.rxh2abh: abhMux = regs68H[ actualRx];
|
|
Nanod.ryh2abh: abhMux = regs68H[ actualRy];
|
|
Nanod.au2Ab: abhMux = auReg[31:16];
|
|
Nanod.aob2Ab: abhMux = aob[31:16];
|
|
Nanod.ath2Abh: abhMux = Ath;
|
|
default: begin abhMux = 'X; abhIdle = 1'b1; end
|
|
endcase
|
|
|
|
end
|
|
|
|
// Source starts driving the bus on T1. Bus holds data until end of T3. Destination latches at T3.
|
|
|
|
// These registers store the first level mux, without bus interconnections.
|
|
// Even when this uses almost to 100 registers, it saves a lot of comb muxing and it is much faster.
|
|
reg [15:0] preAbh, preAbl, preAbd;
|
|
reg [15:0] preDbh, preDbl, preDbd;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
|
|
// Register first level mux at T1
|
|
if( enT1) begin
|
|
{preAbh, preAbl, preAbd} <= { abhMux, ablMux, abdMux};
|
|
{preDbh, preDbl, preDbd} <= { dbhMux, dblMux, dbdMux};
|
|
end
|
|
|
|
// Process bus interconnection at T2. Many combinations only used on DIV
|
|
// We use a simple method. If a specific bus segment is not driven we know that it should get data from a neighbour segment.
|
|
// In some cases this is not true and the segment is really idle without any destination. But then it doesn't matter.
|
|
|
|
if( enT2) begin
|
|
if( Nanod.extAbh)
|
|
Abh <= { 16{ ablIdle ? preAbd[ 15] : preAbl[ 15] }};
|
|
else if( abhIdle)
|
|
Abh <= ablIdle ? preAbd : preAbl;
|
|
else
|
|
Abh <= preAbh;
|
|
|
|
if( ~ablIdle)
|
|
Abl <= preAbl;
|
|
else
|
|
Abl <= Nanod.ablAbh ? preAbh : preAbd;
|
|
|
|
Abd <= ~abdIdle ? preAbd : ablIdle ? preAbh : preAbl;
|
|
|
|
if( Nanod.extDbh)
|
|
Dbh <= { 16{ dblIdle ? preDbd[ 15] : preDbl[ 15] }};
|
|
else if( dbhIdle)
|
|
Dbh <= dblIdle ? preDbd : preDbl;
|
|
else
|
|
Dbh <= preDbh;
|
|
|
|
if( ~dblIdle)
|
|
Dbl <= preDbl;
|
|
else
|
|
Dbl <= Nanod.dblDbh ? preDbh : preDbd;
|
|
|
|
Dbd <= ~dbdIdle ? preDbd: dblIdle ? preDbh : preDbl;
|
|
|
|
/*
|
|
Dbl <= dblMux; Dbh <= dbhMux;
|
|
Abd <= abdMux; Dbd <= dbdMux;
|
|
Abh <= abhMux; Abl <= ablMux; */
|
|
end
|
|
end
|
|
|
|
// AOB
|
|
//
|
|
// Originally change on T1. We do on T2, only then the output is enabled anyway.
|
|
//
|
|
// AOB[0] is used for address error. But even when raises on T1, seems not actually used until T2 or possibly T3.
|
|
// It is used on T1 when deasserted at the BSER exception ucode. Probably deassertion timing is not critical.
|
|
// But in that case (at BSER), AOB is loaded from AU, so we can safely transfer on T1.
|
|
|
|
// We need to take directly from first level muxes that are updated and T1
|
|
|
|
wire au2Aob = Nanod.au2Aob | (Nanod.au2Db & Nanod.db2Aob);
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
// UNIQUE IF !
|
|
|
|
if( enT1 & au2Aob) // From AU we do can on T1
|
|
aob <= auReg;
|
|
else if( enT2) begin
|
|
if( Nanod.db2Aob)
|
|
aob <= { preDbh, ~dblIdle ? preDbl : preDbd};
|
|
else if( Nanod.ab2Aob)
|
|
aob <= { preAbh, ~ablIdle ? preAbl : preAbd};
|
|
end
|
|
end
|
|
|
|
assign eab = aob[23:1];
|
|
assign aob0 = aob[0];
|
|
|
|
// AU
|
|
logic [31:0] auInpMux;
|
|
|
|
// `ifdef ALW_COMB_BUG
|
|
// Old Modelsim bug. Doesn't update ouput always. Need excplicit sensitivity list !?
|
|
// always @( Nanod.auCntrl) begin
|
|
|
|
always_comb begin
|
|
unique case( Nanod.auCntrl)
|
|
3'b000: auInpMux = 0;
|
|
3'b001: auInpMux = byteNotSpAlign | Nanod.noSpAlign ? 1 : 2; // +1/+2
|
|
3'b010: auInpMux = -4;
|
|
3'b011: auInpMux = { Abh, Abl};
|
|
3'b100: auInpMux = 2;
|
|
3'b101: auInpMux = 4;
|
|
3'b110: auInpMux = -2;
|
|
3'b111: auInpMux = byteNotSpAlign | Nanod.noSpAlign ? -1 : -2; // -1/-2
|
|
default: auInpMux = 'X;
|
|
endcase
|
|
end
|
|
|
|
// Simulation problem
|
|
// Sometimes (like in MULM1) DBH is not set. AU is used in these cases just as a 6 bits counter testing if bits 5-0 are zero.
|
|
// But when adding something like 32'hXXXX0000, the simulator (incorrectly) will set *all the 32 bits* of the result as X.
|
|
|
|
// synthesis translate_off
|
|
`define SIMULBUGX32 1
|
|
wire [16:0] aulow = Dbl + auInpMux[15:0];
|
|
wire [31:0] auResult = {Dbh + auInpMux[31:16] + aulow[16], aulow[15:0]};
|
|
// synthesis translate_on
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp)
|
|
auReg <= '0;
|
|
else if( enT3 & Nanod.auClkEn)
|
|
`ifdef SIMULBUGX32
|
|
auReg <= auResult;
|
|
`else
|
|
auReg <= { Dbh, Dbl } + auInpMux;
|
|
`endif
|
|
end
|
|
|
|
|
|
// Main A/D registers
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT3) begin
|
|
if( Nanod.dbl2rxl | Nanod.abl2rxl) begin
|
|
if( ~rxIsAreg) begin
|
|
if( Nanod.dbl2rxl) regs68L[ actualRx] <= Dbd;
|
|
else if( abdIsByte) regs68L[ actualRx][7:0] <= Abd[7:0];
|
|
else regs68L[ actualRx] <= Abd;
|
|
end
|
|
else
|
|
regs68L[ actualRx] <= Nanod.dbl2rxl ? Dbl : Abl;
|
|
end
|
|
|
|
if( Nanod.dbl2ryl | Nanod.abl2ryl) begin
|
|
if( ~ryIsAreg) begin
|
|
if( Nanod.dbl2ryl) regs68L[ actualRy] <= Dbd;
|
|
else if( abdIsByte) regs68L[ actualRy][7:0] <= Abd[7:0];
|
|
else regs68L[ actualRy] <= Abd;
|
|
end
|
|
else
|
|
regs68L[ actualRy] <= Nanod.dbl2ryl ? Dbl : Abl;
|
|
end
|
|
|
|
// High registers are easier. Both A & D on the same buses, and not byte ops.
|
|
if( Nanod.dbh2rxh | Nanod.abh2rxh)
|
|
regs68H[ actualRx] <= Nanod.dbh2rxh ? Dbh : Abh;
|
|
if( Nanod.dbh2ryh | Nanod.abh2ryh)
|
|
regs68H[ actualRy] <= Nanod.dbh2ryh ? Dbh : Abh;
|
|
|
|
end
|
|
end
|
|
|
|
// PC & AT
|
|
reg dbl2Pcl, dbh2Pch, abh2Pch, abl2Pcl;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset) begin
|
|
{ dbl2Pcl, dbh2Pch, abh2Pch, abl2Pcl } <= '0;
|
|
|
|
Pcl2Dbl <= 1'b0;
|
|
Pch2Dbh <= 1'b0;
|
|
Pcl2Abl <= 1'b0;
|
|
Pch2Abh <= 1'b0;
|
|
end
|
|
else if( enT4) begin // Must latch on T4 !
|
|
dbl2Pcl <= Nanod.dbl2reg & Nanod.pcldbl;
|
|
dbh2Pch <= Nanod.dbh2reg & Nanod.pchdbh;
|
|
abh2Pch <= Nanod.abh2reg & Nanod.pchabh;
|
|
abl2Pcl <= Nanod.abl2reg & Nanod.pclabl;
|
|
|
|
Pcl2Dbl <= Nanod.reg2dbl & Nanod.pcldbl;
|
|
Pch2Dbh <= Nanod.reg2dbh & Nanod.pchdbh;
|
|
Pcl2Abl <= Nanod.reg2abl & Nanod.pclabl;
|
|
Pch2Abh <= Nanod.reg2abh & Nanod.pchabh;
|
|
end
|
|
|
|
// Unique IF !!!
|
|
if( enT1 & Nanod.au2Pc)
|
|
PcL <= auReg[15:0];
|
|
else if( enT3) begin
|
|
if( dbl2Pcl)
|
|
PcL <= Dbl;
|
|
else if( abl2Pcl)
|
|
PcL <= Abl;
|
|
end
|
|
|
|
// Unique IF !!!
|
|
if( enT1 & Nanod.au2Pc)
|
|
PcH <= auReg[31:16];
|
|
else if( enT3) begin
|
|
if( dbh2Pch)
|
|
PcH <= Dbh;
|
|
else if( abh2Pch)
|
|
PcH <= Abh;
|
|
end
|
|
|
|
// Unique IF !!!
|
|
if( enT3) begin
|
|
if( Nanod.dbl2Atl)
|
|
Atl <= Dbl;
|
|
else if( Nanod.abl2Atl)
|
|
Atl <= Abl;
|
|
end
|
|
|
|
// Unique IF !!!
|
|
if( enT3) begin
|
|
if( Nanod.abh2Ath)
|
|
Ath <= Abh;
|
|
else if( Nanod.dbh2Ath)
|
|
Ath <= Dbh;
|
|
end
|
|
|
|
end
|
|
|
|
// Movem reg mask priority encoder
|
|
|
|
wire rmIdle;
|
|
logic [3:0] prHbit;
|
|
logic [15:0] prenLatch;
|
|
|
|
// Invert reg order for predecrement mode
|
|
assign prenEmpty = (~| prenLatch);
|
|
pren rmPren( .mask( prenLatch), .hbit (prHbit));
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
// Cheating: PREN always loaded from DBIN
|
|
// Must be on T1 to branch earlier if reg mask is empty!
|
|
if( enT1 & Nanod.abl2Pren)
|
|
prenLatch <= dbin;
|
|
else if( enT3 & Nanod.updPren) begin
|
|
prenLatch [prHbit] <= 1'b0;
|
|
movemRx <= Irdecod.movemPreDecr ? ~prHbit : prHbit;
|
|
end
|
|
end
|
|
|
|
// DCR
|
|
wire [15:0] dcrCode;
|
|
|
|
wire [3:0] dcrInput = abdIsByte ? { 1'b0, Abd[ 2:0]} : Abd[ 3:0];
|
|
onehotEncoder4 dcrDecoder( .bin( dcrInput), .bitMap( dcrCode));
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp)
|
|
dcr4 <= '0;
|
|
else if( enT3 & Nanod.abd2Dcr) begin
|
|
dcrOutput <= dcrCode;
|
|
dcr4 <= Abd[4];
|
|
end
|
|
end
|
|
|
|
// ALUB
|
|
reg [15:0] alub;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT3) begin
|
|
// UNIQUE IF !!
|
|
if( Nanod.dbd2Alub)
|
|
alub <= Dbd;
|
|
else if( Nanod.abd2Alub)
|
|
alub <= Abd; // abdIsByte affects this !!??
|
|
end
|
|
end
|
|
|
|
wire alueClkEn = enT3 & Nanod.dbd2Alue;
|
|
|
|
// DOB/DBIN/IRC
|
|
|
|
logic [15:0] dobInput;
|
|
wire dobIdle = (~| Nanod.dobCtrl);
|
|
|
|
always_comb begin
|
|
unique case (Nanod.dobCtrl)
|
|
NANO_DOB_ADB: dobInput = Abd;
|
|
NANO_DOB_DBD: dobInput = Dbd;
|
|
NANO_DOB_ALU: dobInput = aluOut;
|
|
default: dobInput = 'X;
|
|
endcase
|
|
end
|
|
|
|
dataIo dataIo( .Clks, .enT1, .enT2, .enT3, .enT4, .Nanod, .Irdecod,
|
|
.iEdb, .dobIdle, .dobInput, .aob0,
|
|
.Irc, .dbin, .oEdb);
|
|
|
|
fx68kAlu alu(
|
|
.clk( Clks.clk), .pwrUp( Clks.pwrUp), .enT1, .enT3, .enT4,
|
|
.ird( Ird),
|
|
.aluColumn( Nanod.aluColumn), .aluAddrCtrl( Nanod.aluActrl),
|
|
.init( Nanod.aluInit), .finish( Nanod.aluFinish), .aluIsByte( Irdecod.isByte),
|
|
.ftu2Ccr( Nanod.ftu2Ccr),
|
|
.alub, .ftu, .alueClkEn, .alue,
|
|
.aluDataCtrl( Nanod.aluDctrl), .iDataBus( Dbd), .iAddrBus(Abd),
|
|
.ze, .aluOut, .ccr);
|
|
|
|
endmodule
|
|
|
|
//
|
|
// Data bus I/O
|
|
// At a separate module because it is a bit complicated and the timing is special.
|
|
// Here we do the low/high byte mux and the special case of MOVEP.
|
|
//
|
|
// Original implementation is rather complex because both the internal and external buses are bidirectional.
|
|
// Input is latched async at the EDB register.
|
|
// We capture directly from the external data bus to the internal registers (IRC & DBIN) on PHI2, starting the external S7 phase, at a T4 internal period.
|
|
|
|
module dataIo( input s_clks Clks,
|
|
input enT1, enT2, enT3, enT4,
|
|
input s_nanod Nanod, input s_irdecod Irdecod,
|
|
input [15:0] iEdb,
|
|
input aob0,
|
|
|
|
input dobIdle,
|
|
input [15:0] dobInput,
|
|
|
|
output logic [15:0] Irc,
|
|
output logic [15:0] dbin,
|
|
output logic [15:0] oEdb
|
|
);
|
|
|
|
reg [15:0] dob;
|
|
|
|
// DBIN/IRC
|
|
|
|
// Timing is different than any other register. We can latch only on the next T4 (bus phase S7).
|
|
// We need to register all control signals correctly because the next ublock will already be started.
|
|
// Can't latch control on T4 because if there are wait states there might be multiple T4 before we latch.
|
|
|
|
reg xToDbin, xToIrc;
|
|
reg dbinNoLow, dbinNoHigh;
|
|
reg byteMux, isByte_T4;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
|
|
// Byte mux control. Can't latch at T1. AOB might be not ready yet.
|
|
// Must latch IRD decode at T1 (or T4). Then combine and latch only at T3.
|
|
|
|
// Can't latch at T3, a new IRD might be loaded already at T1.
|
|
// Ok to latch at T4 if combination latched then at T3
|
|
if( enT4)
|
|
isByte_T4 <= Irdecod.isByte; // Includes MOVEP from mem, we could OR it here
|
|
|
|
if( enT3) begin
|
|
dbinNoHigh <= Nanod.noHighByte;
|
|
dbinNoLow <= Nanod.noLowByte;
|
|
byteMux <= Nanod.busByte & isByte_T4 & ~aob0;
|
|
end
|
|
|
|
if( enT1) begin
|
|
// If on wait states, we continue latching until next T1
|
|
xToDbin <= 1'b0;
|
|
xToIrc <= 1'b0;
|
|
end
|
|
else if( enT3) begin
|
|
xToDbin <= Nanod.todbin;
|
|
xToIrc <= Nanod.toIrc;
|
|
end
|
|
|
|
// Capture on T4 of the next ucycle
|
|
// If there are wait states, we keep capturing every PHI2 until the next T1
|
|
|
|
if( xToIrc & Clks.enPhi2)
|
|
Irc <= iEdb;
|
|
if( xToDbin & Clks.enPhi2) begin
|
|
// Original connects both halves of EDB.
|
|
if( ~dbinNoLow)
|
|
dbin[ 7:0] <= byteMux ? iEdb[ 15:8] : iEdb[7:0];
|
|
if( ~dbinNoHigh)
|
|
dbin[ 15:8] <= ~byteMux & dbinNoLow ? iEdb[ 7:0] : iEdb[ 15:8];
|
|
end
|
|
end
|
|
|
|
// DOB
|
|
logic byteCycle;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
// Originaly on T1. Transfer to internal EDB also on T1 (stays enabled upto the next T1). But only on T4 (S3) output enables.
|
|
// It is safe to do on T3, then, but control signals if derived from IRD must be registered.
|
|
// Originally control signals are not registered.
|
|
|
|
// Wait states don't affect DOB operation that is done at the start of the bus cycle.
|
|
|
|
if( enT4)
|
|
byteCycle <= Nanod.busByte & Irdecod.isByte; // busIsByte but not MOVEP
|
|
|
|
// Originally byte low/high interconnect is done at EDB, not at DOB.
|
|
if( enT3 & ~dobIdle) begin
|
|
dob[7:0] <= Nanod.noLowByte ? dobInput[15:8] : dobInput[ 7:0];
|
|
dob[15:8] <= (byteCycle | Nanod.noHighByte) ? dobInput[ 7:0] : dobInput[15:8];
|
|
end
|
|
end
|
|
assign oEdb = dob;
|
|
|
|
endmodule
|
|
|
|
|
|
// Provides ucode routine entries (A1/A3) for each opcode
|
|
// Also checks for illegal opcode and priv violation
|
|
|
|
// This is one of the slowest part of the processor.
|
|
// But no need to optimize or pipeline because the result for a1-a3 is not needed until at least 4 cycles.
|
|
// IR updated at the least one microinstruction earlier.
|
|
// Just need to configure the timing analizer correctly.
|
|
// isPriv, isIllegal might be needed as soon as two cycles later
|
|
|
|
|
|
module uaddrDecode(
|
|
input [15:0] opcode,
|
|
output [UADDR_WIDTH-1:0] a1, a2, a3,
|
|
output logic isPriv, isIllegal, isLineA, isLineF,
|
|
output [15:0] lineBmap);
|
|
|
|
wire [3:0] line = opcode[15:12];
|
|
logic [3:0] eaCol, movEa;
|
|
|
|
onehotEncoder4 irLineDecod( line, lineBmap);
|
|
|
|
assign isLineA = lineBmap[ 'hA];
|
|
assign isLineF = lineBmap[ 'hF];
|
|
|
|
pla_lined pla_lined( .movEa( movEa), .col( eaCol),
|
|
.opcode( opcode), .lineBmap( lineBmap),
|
|
.palIll( isIllegal), .plaA1( a1), .plaA2( a2), .plaA3( a3) );
|
|
|
|
// ea decoding
|
|
assign eaCol = eaDecode( opcode[ 5:0]);
|
|
assign movEa = eaDecode( {opcode[ 8:6], opcode[ 11:9]} );
|
|
|
|
// EA decode
|
|
function [3:0] eaDecode;
|
|
input [5:0] eaBits;
|
|
begin
|
|
unique case( eaBits[ 5:3])
|
|
3'b111:
|
|
case( eaBits[ 2:0])
|
|
3'b000: eaDecode = 7; // Absolute short
|
|
3'b001: eaDecode = 8; // Absolute long
|
|
3'b010: eaDecode = 9; // PC displacement
|
|
3'b011: eaDecode = 10; // PC offset
|
|
3'b100: eaDecode = 11; // Immediate
|
|
default: eaDecode = 12; // Invalid
|
|
endcase
|
|
|
|
default: eaDecode = eaBits[5:3]; // Register based EAs
|
|
endcase
|
|
end
|
|
endfunction
|
|
|
|
|
|
/*
|
|
Privileged instructions:
|
|
|
|
ANDI/EORI/ORI SR
|
|
MOVE to SR
|
|
MOVE to/from USP
|
|
RESET
|
|
RTE
|
|
STOP
|
|
*/
|
|
|
|
always_comb begin
|
|
unique case( lineBmap)
|
|
|
|
// ori/andi/eori SR
|
|
'h01: isPriv = ((opcode & 16'hf5ff) == 16'h007c);
|
|
|
|
'h10:
|
|
begin
|
|
// No priority !!!
|
|
if( (opcode & 16'hffc0) == 16'h46c0) // move to sr
|
|
isPriv = 1'b1;
|
|
|
|
else if( (opcode & 16'hfff0) == 16'h4e60) // move usp
|
|
isPriv = 1'b1;
|
|
|
|
else if( opcode == 16'h4e70 || // reset
|
|
opcode == 16'h4e73 || // rte
|
|
opcode == 16'h4e72) // stop
|
|
isPriv = 1'b1;
|
|
else
|
|
isPriv = 1'b0;
|
|
end
|
|
|
|
default: isPriv = 1'b0;
|
|
endcase
|
|
end
|
|
|
|
|
|
endmodule
|
|
|
|
// bin to one-hot, 4 bits to 16-bit bitmap
|
|
module onehotEncoder4( input [3:0] bin, output reg [15:0] bitMap);
|
|
always_comb begin
|
|
case( bin)
|
|
'b0000: bitMap = 16'h0001;
|
|
'b0001: bitMap = 16'h0002;
|
|
'b0010: bitMap = 16'h0004;
|
|
'b0011: bitMap = 16'h0008;
|
|
'b0100: bitMap = 16'h0010;
|
|
'b0101: bitMap = 16'h0020;
|
|
'b0110: bitMap = 16'h0040;
|
|
'b0111: bitMap = 16'h0080;
|
|
'b1000: bitMap = 16'h0100;
|
|
'b1001: bitMap = 16'h0200;
|
|
'b1010: bitMap = 16'h0400;
|
|
'b1011: bitMap = 16'h0800;
|
|
'b1100: bitMap = 16'h1000;
|
|
'b1101: bitMap = 16'h2000;
|
|
'b1110: bitMap = 16'h4000;
|
|
'b1111: bitMap = 16'h8000;
|
|
endcase
|
|
end
|
|
endmodule
|
|
|
|
// priority encoder
|
|
// used by MOVEM regmask
|
|
// this might benefit from device specific features
|
|
// MOVEM doesn't need speed, will read the result 2 CPU cycles after each update.
|
|
module pren( mask, hbit);
|
|
parameter size = 16;
|
|
parameter outbits = 4;
|
|
|
|
input [size-1:0] mask;
|
|
output reg [outbits-1:0] hbit;
|
|
// output reg idle;
|
|
|
|
always @( mask) begin
|
|
integer i;
|
|
hbit = 0;
|
|
// idle = 1;
|
|
for( i = size-1; i >= 0; i = i - 1) begin
|
|
if( mask[ i]) begin
|
|
hbit = i;
|
|
// idle = 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
// Microcode sequencer
|
|
|
|
module sequencer( input s_clks Clks, input enT3,
|
|
input [UROM_WIDTH-1:0] microLatch,
|
|
input A0Err, BerrA, busAddrErr, Spuria, Avia,
|
|
input Tpend, intPend, isIllegal, isPriv, excRst, isLineA, isLineF,
|
|
input [15:0] psw,
|
|
input prenEmpty, au05z, dcr4, ze, i11,
|
|
input [1:0] alue01,
|
|
input [15:0] Ird,
|
|
input [UADDR_WIDTH-1:0] a1, a2, a3,
|
|
output logic [3:0] tvn,
|
|
output logic [UADDR_WIDTH-1:0] nma);
|
|
|
|
logic [UADDR_WIDTH-1:0] uNma;
|
|
logic [UADDR_WIDTH-1:0] grp1Nma;
|
|
logic [1:0] c0c1;
|
|
reg a0Rst;
|
|
wire A0Sel;
|
|
wire inGrp0Exc;
|
|
|
|
// assign nma = Clks.extReset ? RSTP0_NMA : (A0Err ? BSER1_NMA : uNma);
|
|
// assign nma = A0Err ? (a0Rst ? RSTP0_NMA : BSER1_NMA) : uNma;
|
|
|
|
// word type I: 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
|
|
// NMA : .. .. 09 08 01 00 05 04 03 02 07 06 .. .. .. .. ..
|
|
|
|
wire [UADDR_WIDTH-1:0] dbNma = { microLatch[ 14:13], microLatch[ 6:5], microLatch[ 10:7], microLatch[ 12:11]};
|
|
|
|
// Group 0 exception.
|
|
// Separated block from regular NMA. Otherwise simulation might depend on order of assigments.
|
|
always_comb begin
|
|
if( A0Err) begin
|
|
if( a0Rst) // Reset
|
|
nma = RSTP0_NMA;
|
|
else if( inGrp0Exc) // Double fault
|
|
nma = HALT1_NMA;
|
|
else // Bus or address error
|
|
nma = BSER1_NMA;
|
|
end
|
|
else
|
|
nma = uNma;
|
|
end
|
|
|
|
always_comb begin
|
|
// Format II (conditional) or I (direct branch)
|
|
if( microLatch[1])
|
|
uNma = { microLatch[ 14:13], c0c1, microLatch[ 10:7], microLatch[ 12:11]};
|
|
else
|
|
case( microLatch[ 3:2])
|
|
0: uNma = dbNma; // DB
|
|
1: uNma = A0Sel ? grp1Nma : a1;
|
|
2: uNma = a2;
|
|
3: uNma = a3;
|
|
endcase
|
|
end
|
|
|
|
// Format II, conditional, NMA decoding
|
|
wire [1:0] enl = { Ird[6], prenEmpty}; // Updated on T3
|
|
|
|
wire [1:0] ms0 = { Ird[8], alue01[0]};
|
|
wire [3:0] m01 = { au05z, Ird[8], alue01};
|
|
wire [1:0] nz1 = { psw[ NF], psw[ ZF]};
|
|
wire [1:0] nv = { psw[ NF], psw[ VF]};
|
|
|
|
logic ccTest;
|
|
wire [4:0] cbc = microLatch[ 6:2]; // CBC bits
|
|
|
|
always_comb begin
|
|
unique case( cbc)
|
|
'h0: c0c1 = {i11, i11}; // W/L offset EA, from IRC
|
|
|
|
'h1: c0c1 = (au05z) ? 2'b01 : 2'b11; // Updated on T3
|
|
'h11: c0c1 = (au05z) ? 2'b00 : 2'b11;
|
|
|
|
'h02: c0c1 = { 1'b0, ~psw[ CF]}; // C used in DIV
|
|
'h12: c0c1 = { 1'b1, ~psw[ CF]};
|
|
|
|
'h03: c0c1 = {psw[ ZF], psw[ ZF]}; // Z used in DIVU
|
|
|
|
'h04: // nz1, used in DIVS
|
|
case( nz1)
|
|
'b00: c0c1 = 2'b10;
|
|
'b10: c0c1 = 2'b01;
|
|
'b01,'b11: c0c1 = 2'b11;
|
|
endcase
|
|
|
|
'h05: c0c1 = {psw[ NF], 1'b1}; // N used in CHK and DIV
|
|
'h15: c0c1 = {1'b1, psw[ NF]};
|
|
|
|
// nz2, used in DIVS (same combination as nz1)
|
|
'h06: c0c1 = { ~nz1[1] & ~nz1[0], 1'b1};
|
|
|
|
'h07: // ms0 used in MUL
|
|
case( ms0)
|
|
'b10, 'b00: c0c1 = 2'b11;
|
|
'b01: c0c1 = 2'b01;
|
|
'b11: c0c1 = 2'b10;
|
|
endcase
|
|
|
|
'h08: // m01 used in MUL
|
|
case( m01)
|
|
'b0000,'b0001,'b0100,'b0111: c0c1 = 2'b11;
|
|
'b0010,'b0011,'b0101: c0c1 = 2'b01;
|
|
'b0110: c0c1 = 2'b10;
|
|
default: c0c1 = 2'b00;
|
|
endcase
|
|
|
|
// Conditional
|
|
'h09: c0c1 = (ccTest) ? 2'b11 : 2'b01;
|
|
'h19: c0c1 = (ccTest) ? 2'b11 : 2'b10;
|
|
|
|
// DCR bit 4 (high or low word)
|
|
'h0c: c0c1 = dcr4 ? 2'b01: 2'b11;
|
|
'h1c: c0c1 = dcr4 ? 2'b10: 2'b11;
|
|
|
|
// DBcc done
|
|
'h0a: c0c1 = ze ? 2'b11 : 2'b00;
|
|
|
|
// nv, used in CHK
|
|
'h0b: c0c1 = (nv == 2'b00) ? 2'b00 : 2'b11;
|
|
|
|
// V, used in trapv
|
|
'h0d: c0c1 = { ~psw[ VF], ~psw[VF]};
|
|
|
|
// enl, combination of pren idle and word/long on IRD
|
|
'h0e,'h1e:
|
|
case( enl)
|
|
2'b00: c0c1 = 'b10;
|
|
2'b10: c0c1 = 'b11;
|
|
// 'hx1 result 00/01 depending on condition 0e/1e
|
|
2'b01,2'b11:
|
|
c0c1 = { 1'b0, microLatch[ 6]};
|
|
endcase
|
|
|
|
default: c0c1 = 'X;
|
|
endcase
|
|
end
|
|
|
|
// CCR conditional
|
|
always_comb begin
|
|
unique case( Ird[ 11:8])
|
|
'h0: ccTest = 1'b1; // T
|
|
'h1: ccTest = 1'b0; // F
|
|
'h2: ccTest = ~psw[ CF] & ~psw[ ZF]; // HI
|
|
'h3: ccTest = psw[ CF] | psw[ZF]; // LS
|
|
'h4: ccTest = ~psw[ CF]; // CC (HS)
|
|
'h5: ccTest = psw[ CF]; // CS (LO)
|
|
'h6: ccTest = ~psw[ ZF]; // NE
|
|
'h7: ccTest = psw[ ZF]; // EQ
|
|
'h8: ccTest = ~psw[ VF]; // VC
|
|
'h9: ccTest = psw[ VF]; // VS
|
|
'ha: ccTest = ~psw[ NF]; // PL
|
|
'hb: ccTest = psw[ NF]; // MI
|
|
'hc: ccTest = (psw[ NF] & psw[ VF]) | (~psw[ NF] & ~psw[ VF]); // GE
|
|
'hd: ccTest = (psw[ NF] & ~psw[ VF]) | (~psw[ NF] & psw[ VF]); // LT
|
|
'he: ccTest = (psw[ NF] & psw[ VF] & ~psw[ ZF]) |
|
|
(~psw[ NF] & ~psw[ VF] & ~psw[ ZF]); // GT
|
|
'hf: ccTest = psw[ ZF] | (psw[ NF] & ~psw[VF]) | (~psw[ NF] & psw[VF]); // LE
|
|
endcase
|
|
end
|
|
|
|
// Exception logic
|
|
logic rTrace, rInterrupt;
|
|
logic rIllegal, rPriv, rLineA, rLineF;
|
|
logic rExcRst, rExcAdrErr, rExcBusErr;
|
|
logic rSpurious, rAutovec;
|
|
wire grp1LatchEn, grp0LatchEn;
|
|
|
|
// Originally control signals latched on T4. Then exception latches updated on T3
|
|
assign grp1LatchEn = microLatch[0] & (microLatch[1] | !microLatch[4]);
|
|
assign grp0LatchEn = microLatch[4] & !microLatch[1];
|
|
|
|
assign inGrp0Exc = rExcRst | rExcBusErr | rExcAdrErr;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( grp0LatchEn & enT3) begin
|
|
rExcRst <= excRst;
|
|
rExcBusErr <= BerrA;
|
|
rExcAdrErr <= busAddrErr;
|
|
rSpurious <= Spuria;
|
|
rAutovec <= Avia;
|
|
end
|
|
|
|
// Update group 1 exception latches
|
|
// Inputs from IR decoder updated on T1 as soon as IR loaded
|
|
// Trace pending updated on T3 at the start of the instruction
|
|
// Interrupt pending on T2
|
|
if( grp1LatchEn & enT3) begin
|
|
rTrace <= Tpend;
|
|
rInterrupt <= intPend;
|
|
rIllegal <= isIllegal & ~isLineA & ~isLineF;
|
|
rLineA <= isLineA;
|
|
rLineF <= isLineF;
|
|
rPriv <= isPriv & !psw[ SF];
|
|
end
|
|
end
|
|
|
|
// exception priority
|
|
always_comb begin
|
|
grp1Nma = TRAC1_NMA;
|
|
if( rExcRst)
|
|
tvn = '0; // Might need to change that to signal in exception
|
|
else if( rExcBusErr | rExcAdrErr)
|
|
tvn = { 1'b1, rExcAdrErr};
|
|
|
|
// Seudo group 0 exceptions. Just for updating TVN
|
|
else if( rSpurious | rAutovec)
|
|
tvn = rSpurious ? TVN_SPURIOUS : TVN_AUTOVEC;
|
|
|
|
else if( rTrace)
|
|
tvn = 9;
|
|
else if( rInterrupt) begin
|
|
tvn = TVN_INTERRUPT;
|
|
grp1Nma = ITLX1_NMA;
|
|
end
|
|
else begin
|
|
unique case( 1'b1) // Can't happen more than one of these
|
|
rIllegal: tvn = 4;
|
|
rPriv: tvn = 8;
|
|
rLineA: tvn = 10;
|
|
rLineF: tvn = 11;
|
|
default: tvn = 1; // Signal no group 0/1 exception
|
|
endcase
|
|
end
|
|
end
|
|
|
|
assign A0Sel = rIllegal | rLineF | rLineA | rPriv | rTrace | rInterrupt;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset)
|
|
a0Rst <= 1'b1;
|
|
else if( enT3)
|
|
a0Rst <= 1'b0;
|
|
end
|
|
|
|
endmodule
|
|
|
|
|
|
//
|
|
// DMA/BUS Arbitration
|
|
//
|
|
|
|
module busArbiter( input s_clks Clks,
|
|
input BRi, BgackI, Halti, bgBlock,
|
|
output busAvail,
|
|
output logic BGn);
|
|
|
|
enum int unsigned { DRESET = 0, DIDLE, D1, D_BR, D_BA, D_BRA, D3, D2} dmaPhase, next;
|
|
|
|
always_comb begin
|
|
case(dmaPhase)
|
|
DRESET: next = DIDLE;
|
|
DIDLE: begin
|
|
if( bgBlock)
|
|
next = DIDLE;
|
|
else if( ~BgackI)
|
|
next = D_BA;
|
|
else if( ~BRi)
|
|
next = D1;
|
|
else
|
|
next = DIDLE;
|
|
end
|
|
|
|
D_BA: begin // Loop while only BGACK asserted, BG negated here
|
|
if( ~BRi & !bgBlock)
|
|
next = D3;
|
|
else if( ~BgackI & !bgBlock)
|
|
next = D_BA;
|
|
else
|
|
next = DIDLE;
|
|
end
|
|
|
|
D1: next = D_BR; // Loop while only BR asserted
|
|
D_BR: next = ~BRi & BgackI ? D_BR : D_BA; // No direct path to IDLE !
|
|
|
|
D3: next = D_BRA;
|
|
D_BRA: begin // Loop while both BR and BGACK asserted
|
|
case( {BgackI, BRi} )
|
|
2'b11: next = DIDLE; // Both deasserted
|
|
2'b10: next = D_BR; // BR asserted only
|
|
2'b01: next = D2; // BGACK asserted only
|
|
2'b00: next = D_BRA; // Stay here while both asserted
|
|
endcase
|
|
end
|
|
|
|
// Might loop here if both deasserted, should normally don't arrive here anyway?
|
|
// D2: next = (BgackI & BRi) | bgBlock ? D2: D_BA;
|
|
|
|
D2: next = D_BA;
|
|
|
|
default: next = DIDLE; // Should not reach here normally
|
|
endcase
|
|
end
|
|
|
|
logic granting;
|
|
always_comb begin
|
|
unique case( next)
|
|
D1, D3, D_BR, D_BRA: granting = 1'b1;
|
|
default: granting = 1'b0;
|
|
endcase
|
|
end
|
|
|
|
reg rGranted;
|
|
assign busAvail = Halti & BRi & BgackI & ~rGranted;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset) begin
|
|
dmaPhase <= DRESET;
|
|
rGranted <= 1'b0;
|
|
end
|
|
else if( Clks.enPhi2) begin
|
|
dmaPhase <= next;
|
|
// Internal signal changed on PHI2
|
|
rGranted <= granting;
|
|
end
|
|
|
|
// External Output changed on PHI1
|
|
if( Clks.extReset)
|
|
BGn <= 1'b1;
|
|
else if( Clks.enPhi1)
|
|
BGn <= ~rGranted;
|
|
|
|
end
|
|
|
|
endmodule
|
|
|
|
module busControl( input s_clks Clks, input enT1, input enT4,
|
|
input permStart, permStop, iStop,
|
|
input aob0,
|
|
input isWrite, isByte, isRmc,
|
|
input busAvail,
|
|
output bgBlock,
|
|
output busAddrErr,
|
|
output waitBusCycle,
|
|
output busStarting, // Asserted during S0
|
|
output logic addrOe, // Asserted from S1 to the end, whole bus cycle except S0
|
|
output bciWrite, // Used for SSW on bus/addr error
|
|
|
|
input rDtack, BeDebounced, Vpai,
|
|
output ASn, output LDSn, output UDSn, eRWn);
|
|
|
|
reg rAS, rLDS, rUDS, rRWn;
|
|
assign ASn = rAS;
|
|
assign LDSn = rLDS;
|
|
assign UDSn = rUDS;
|
|
assign eRWn = rRWn;
|
|
|
|
//reg dataOe;
|
|
|
|
reg bcPend;
|
|
reg isWriteReg, bciByte, isRmcReg, wendReg;
|
|
assign bciWrite = isWriteReg;
|
|
reg addrOeDelay;
|
|
reg isByteT4;
|
|
|
|
wire canStart, busEnd;
|
|
wire bcComplete, bcReset;
|
|
|
|
wire isRcmReset = bcComplete & bcReset & isRmcReg;
|
|
|
|
assign busAddrErr = aob0 & ~bciByte;
|
|
|
|
// Bus retry not really supported.
|
|
// It's BERR and HALT and not address error, and not read-modify cycle.
|
|
wire busRetry = ~busAddrErr & 1'b0;
|
|
|
|
enum int unsigned { SRESET = 0, SIDLE, S0, S2, S4, S6, SRMC_RES} busPhase, next;
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset)
|
|
busPhase <= SRESET;
|
|
else if( Clks.enPhi1)
|
|
busPhase <= next;
|
|
end
|
|
|
|
always_comb begin
|
|
case( busPhase)
|
|
SRESET: next = SIDLE;
|
|
SRMC_RES: next = SIDLE; // Single cycle special state when read phase of RMC reset
|
|
S0: next = S2;
|
|
S2: next = S4;
|
|
S4: next = busEnd ? S6 : S4;
|
|
S6: next = isRcmReset ? SRMC_RES : (canStart ? S0 : SIDLE);
|
|
SIDLE: next = canStart ? S0 : SIDLE;
|
|
default: next = SIDLE;
|
|
endcase
|
|
end
|
|
|
|
// Idle phase of RMC bus cycle. Might be better to just add a new state
|
|
wire rmcIdle = (busPhase == SIDLE) & ~ASn & isRmcReg;
|
|
|
|
assign canStart = (busAvail | rmcIdle) & (bcPend | permStart) & !busRetry & !bcReset;
|
|
|
|
wire busEnding = (next == SIDLE) | (next == S0);
|
|
|
|
assign busStarting = (busPhase == S0);
|
|
|
|
// term signal (DTACK, BERR, VPA, adress error)
|
|
assign busEnd = ~rDtack | iStop;
|
|
|
|
// bcComplete asserted on raising edge of S6 (together with SNC).
|
|
assign bcComplete = (busPhase == S6);
|
|
|
|
// Clear bus info latch on completion (regular or aborted) and no bus retry (and not PHI1).
|
|
// bciClear asserted half clock later on PHI2, and bci latches cleared async concurrently
|
|
wire bciClear = bcComplete & ~busRetry;
|
|
|
|
// Reset on reset or (berr & berrDelay & (not halt or rmc) & not 6800 & in bus cycle) (and not PHI1)
|
|
assign bcReset = Clks.extReset | (addrOeDelay & BeDebounced & Vpai);
|
|
|
|
// Enable uclock only on S6 (S8 on Bus Error) or not bciPermStop
|
|
assign waitBusCycle = wendReg & !bcComplete;
|
|
|
|
// Block Bus Grant when starting new bus cycle. But No need if AS already asserted (read phase of RMC)
|
|
// Except that when that RMC phase aborted on bus error, it's asserted one cycle later!
|
|
assign bgBlock = ((busPhase == S0) & ASn) | (busPhase == SRMC_RES);
|
|
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.extReset) begin
|
|
addrOe <= 1'b0;
|
|
end
|
|
else if( Clks.enPhi2 & ( busPhase == S0)) // From S1, whole bus cycle except S0
|
|
addrOe <= 1'b1;
|
|
else if( Clks.enPhi1 & (busPhase == SRMC_RES))
|
|
addrOe <= 1'b0;
|
|
else if( Clks.enPhi1 & ~isRmcReg & busEnding)
|
|
addrOe <= 1'b0;
|
|
|
|
if( Clks.enPhi1)
|
|
addrOeDelay <= addrOe;
|
|
|
|
if( Clks.extReset) begin
|
|
rAS <= 1'b1;
|
|
rUDS <= 1'b1;
|
|
rLDS <= 1'b1;
|
|
rRWn <= 1'b1;
|
|
//dataOe <= '0;
|
|
end
|
|
else begin
|
|
/*
|
|
if( Clks.enPhi2 & isWriteReg & (busPhase == S2))
|
|
dataOe <= 1'b1;
|
|
else if( Clks.enPhi1 & (busEnding | (busPhase == SIDLE)) )
|
|
dataOe <= 1'b0;
|
|
*/
|
|
if( Clks.enPhi1 & busEnding)
|
|
rRWn <= 1'b1;
|
|
else if( Clks.enPhi1 & isWriteReg) begin
|
|
// Unlike LDS/UDS Asserted even in address error
|
|
if( (busPhase == S0) & isWriteReg)
|
|
rRWn <= 1'b0;
|
|
end
|
|
|
|
// AS. Actually follows addrOe half cycle later!
|
|
if( Clks.enPhi1 & (busPhase == S0))
|
|
rAS <= 1'b0;
|
|
else if( Clks.enPhi2 & (busPhase == SRMC_RES)) // Bus error on read phase of RMC. Deasserted one cycle later
|
|
rAS <= 1'b1;
|
|
else if( Clks.enPhi2 & bcComplete & ~SRMC_RES)
|
|
if( ~isRmcReg) // Keep AS asserted on the IDLE phase of RMC
|
|
rAS <= 1'b1;
|
|
|
|
if( Clks.enPhi1 & (busPhase == S0)) begin
|
|
if( ~isWriteReg & !busAddrErr) begin
|
|
rUDS <= ~(~bciByte | ~aob0);
|
|
rLDS <= ~(~bciByte | aob0);
|
|
end
|
|
end
|
|
else if( Clks.enPhi1 & isWriteReg & (busPhase == S2) & !busAddrErr) begin
|
|
rUDS <= ~(~bciByte | ~aob0);
|
|
rLDS <= ~(~bciByte | aob0);
|
|
end
|
|
else if( Clks.enPhi2 & bcComplete) begin
|
|
rUDS <= 1'b1;
|
|
rLDS <= 1'b1;
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
// Bus cycle info latch. Needed because uinstr might change if the bus is busy and we must wait.
|
|
// Note that urom advances even on wait states. It waits *after* updating urom and nanorom latches.
|
|
// Even without wait states, ublocks of type ir (init reading) will not wait for bus completion.
|
|
// Originally latched on (permStart AND T1).
|
|
|
|
// Bus cycle info latch: isRead, isByte, read-modify-cycle, and permStart (bus cycle pending). Some previously latched on T4?
|
|
// permStop also latched, but unconditionally on T1
|
|
|
|
// Might make more sense to register this outside this module
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( enT4) begin
|
|
isByteT4 <= isByte;
|
|
end
|
|
end
|
|
|
|
// Bus Cycle Info Latch
|
|
always_ff @( posedge Clks.clk) begin
|
|
if( Clks.pwrUp) begin
|
|
bcPend <= 1'b0;
|
|
wendReg <= 1'b0;
|
|
isWriteReg <= 1'b0;
|
|
bciByte <= 1'b0;
|
|
isRmcReg <= 1'b0;
|
|
end
|
|
|
|
else if( Clks.enPhi2 & (bciClear | bcReset)) begin
|
|
bcPend <= 1'b0;
|
|
wendReg <= 1'b0;
|
|
end
|
|
else begin
|
|
if( enT1 & permStart) begin
|
|
isWriteReg <= isWrite;
|
|
bciByte <= isByteT4;
|
|
isRmcReg <= isRmc & ~isWrite; // We need special case the end of the read phase only.
|
|
bcPend <= 1'b1;
|
|
end
|
|
if( enT1)
|
|
wendReg <= permStop;
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
//
|
|
// microrom and nanorom instantiation
|
|
//
|
|
// There is bit of wasting of resources here. An extra registering pipeline happens that is not needed.
|
|
// This is just for the purpose of helping inferring block RAM using pure generic code. Inferring RAM is important for performance.
|
|
// Might be more efficient to use vendor specific features such as clock enable.
|
|
//
|
|
|
|
module uRom( input clk, input [UADDR_WIDTH-1:0] microAddr, output logic [UROM_WIDTH-1:0] microOutput);
|
|
reg [UROM_WIDTH-1:0] uRam[ UROM_DEPTH];
|
|
initial begin
|
|
$readmemb("microrom.mem", uRam);
|
|
end
|
|
|
|
always_ff @( posedge clk)
|
|
microOutput <= uRam[ microAddr];
|
|
endmodule
|
|
|
|
|
|
module nanoRom( input clk, input [NADDR_WIDTH-1:0] nanoAddr, output logic [NANO_WIDTH-1:0] nanoOutput);
|
|
reg [NANO_WIDTH-1:0] nRam[ NANO_DEPTH];
|
|
initial begin
|
|
$readmemb("nanorom.mem", nRam);
|
|
end
|
|
|
|
always_ff @( posedge clk)
|
|
nanoOutput <= nRam[ nanoAddr];
|
|
endmodule
|
|
|
|
// Translate uaddr to nanoaddr
|
|
module microToNanoAddr(
|
|
input [UADDR_WIDTH-1:0] uAddr,
|
|
output [NADDR_WIDTH-1:0] orgAddr);
|
|
|
|
wire [UADDR_WIDTH-1:2] baseAddr = uAddr[UADDR_WIDTH-1:2];
|
|
logic [NADDR_WIDTH-1:2] orgBase;
|
|
assign orgAddr = { orgBase, uAddr[1:0]};
|
|
|
|
always @( baseAddr)
|
|
begin
|
|
// nano ROM (136 addresses)
|
|
case( baseAddr)
|
|
|
|
'h00: orgBase = 7'h0 ;
|
|
'h01: orgBase = 7'h1 ;
|
|
'h02: orgBase = 7'h2 ;
|
|
'h03: orgBase = 7'h2 ;
|
|
'h08: orgBase = 7'h3 ;
|
|
'h09: orgBase = 7'h4 ;
|
|
'h0A: orgBase = 7'h5 ;
|
|
'h0B: orgBase = 7'h5 ;
|
|
'h10: orgBase = 7'h6 ;
|
|
'h11: orgBase = 7'h7 ;
|
|
'h12: orgBase = 7'h8 ;
|
|
'h13: orgBase = 7'h8 ;
|
|
'h18: orgBase = 7'h9 ;
|
|
'h19: orgBase = 7'hA ;
|
|
'h1A: orgBase = 7'hB ;
|
|
'h1B: orgBase = 7'hB ;
|
|
'h20: orgBase = 7'hC ;
|
|
'h21: orgBase = 7'hD ;
|
|
'h22: orgBase = 7'hE ;
|
|
'h23: orgBase = 7'hD ;
|
|
'h28: orgBase = 7'hF ;
|
|
'h29: orgBase = 7'h10 ;
|
|
'h2A: orgBase = 7'h11 ;
|
|
'h2B: orgBase = 7'h10 ;
|
|
'h30: orgBase = 7'h12 ;
|
|
'h31: orgBase = 7'h13 ;
|
|
'h32: orgBase = 7'h14 ;
|
|
'h33: orgBase = 7'h14 ;
|
|
'h38: orgBase = 7'h15 ;
|
|
'h39: orgBase = 7'h16 ;
|
|
'h3A: orgBase = 7'h17 ;
|
|
'h3B: orgBase = 7'h17 ;
|
|
'h40: orgBase = 7'h18 ;
|
|
'h41: orgBase = 7'h18 ;
|
|
'h42: orgBase = 7'h18 ;
|
|
'h43: orgBase = 7'h18 ;
|
|
'h44: orgBase = 7'h19 ;
|
|
'h45: orgBase = 7'h19 ;
|
|
'h46: orgBase = 7'h19 ;
|
|
'h47: orgBase = 7'h19 ;
|
|
'h48: orgBase = 7'h1A ;
|
|
'h49: orgBase = 7'h1A ;
|
|
'h4A: orgBase = 7'h1A ;
|
|
'h4B: orgBase = 7'h1A ;
|
|
'h4C: orgBase = 7'h1B ;
|
|
'h4D: orgBase = 7'h1B ;
|
|
'h4E: orgBase = 7'h1B ;
|
|
'h4F: orgBase = 7'h1B ;
|
|
'h54: orgBase = 7'h1C ;
|
|
'h55: orgBase = 7'h1D ;
|
|
'h56: orgBase = 7'h1E ;
|
|
'h57: orgBase = 7'h1F ;
|
|
'h5C: orgBase = 7'h20 ;
|
|
'h5D: orgBase = 7'h21 ;
|
|
'h5E: orgBase = 7'h22 ;
|
|
'h5F: orgBase = 7'h23 ;
|
|
'h70: orgBase = 7'h24 ;
|
|
'h71: orgBase = 7'h24 ;
|
|
'h72: orgBase = 7'h24 ;
|
|
'h73: orgBase = 7'h24 ;
|
|
'h74: orgBase = 7'h24 ;
|
|
'h75: orgBase = 7'h24 ;
|
|
'h76: orgBase = 7'h24 ;
|
|
'h77: orgBase = 7'h24 ;
|
|
'h78: orgBase = 7'h25 ;
|
|
'h79: orgBase = 7'h25 ;
|
|
'h7A: orgBase = 7'h25 ;
|
|
'h7B: orgBase = 7'h25 ;
|
|
'h7C: orgBase = 7'h25 ;
|
|
'h7D: orgBase = 7'h25 ;
|
|
'h7E: orgBase = 7'h25 ;
|
|
'h7F: orgBase = 7'h25 ;
|
|
'h84: orgBase = 7'h26 ;
|
|
'h85: orgBase = 7'h27 ;
|
|
'h86: orgBase = 7'h28 ;
|
|
'h87: orgBase = 7'h29 ;
|
|
'h8C: orgBase = 7'h2A ;
|
|
'h8D: orgBase = 7'h2B ;
|
|
'h8E: orgBase = 7'h2C ;
|
|
'h8F: orgBase = 7'h2D ;
|
|
'h94: orgBase = 7'h2E ;
|
|
'h95: orgBase = 7'h2F ;
|
|
'h96: orgBase = 7'h30 ;
|
|
'h97: orgBase = 7'h31 ;
|
|
'h9C: orgBase = 7'h32 ;
|
|
'h9D: orgBase = 7'h33 ;
|
|
'h9E: orgBase = 7'h34 ;
|
|
'h9F: orgBase = 7'h35 ;
|
|
'hA4: orgBase = 7'h36 ;
|
|
'hA5: orgBase = 7'h36 ;
|
|
'hA6: orgBase = 7'h37 ;
|
|
'hA7: orgBase = 7'h37 ;
|
|
'hAC: orgBase = 7'h38 ;
|
|
'hAD: orgBase = 7'h38 ;
|
|
'hAE: orgBase = 7'h39 ;
|
|
'hAF: orgBase = 7'h39 ;
|
|
'hB4: orgBase = 7'h3A ;
|
|
'hB5: orgBase = 7'h3A ;
|
|
'hB6: orgBase = 7'h3B ;
|
|
'hB7: orgBase = 7'h3B ;
|
|
'hBC: orgBase = 7'h3C ;
|
|
'hBD: orgBase = 7'h3C ;
|
|
'hBE: orgBase = 7'h3D ;
|
|
'hBF: orgBase = 7'h3D ;
|
|
'hC0: orgBase = 7'h3E ;
|
|
'hC1: orgBase = 7'h3F ;
|
|
'hC2: orgBase = 7'h40 ;
|
|
'hC3: orgBase = 7'h41 ;
|
|
'hC8: orgBase = 7'h42 ;
|
|
'hC9: orgBase = 7'h43 ;
|
|
'hCA: orgBase = 7'h44 ;
|
|
'hCB: orgBase = 7'h45 ;
|
|
'hD0: orgBase = 7'h46 ;
|
|
'hD1: orgBase = 7'h47 ;
|
|
'hD2: orgBase = 7'h48 ;
|
|
'hD3: orgBase = 7'h49 ;
|
|
'hD8: orgBase = 7'h4A ;
|
|
'hD9: orgBase = 7'h4B ;
|
|
'hDA: orgBase = 7'h4C ;
|
|
'hDB: orgBase = 7'h4D ;
|
|
'hE0: orgBase = 7'h4E ;
|
|
'hE1: orgBase = 7'h4E ;
|
|
'hE2: orgBase = 7'h4F ;
|
|
'hE3: orgBase = 7'h4F ;
|
|
'hE8: orgBase = 7'h50 ;
|
|
'hE9: orgBase = 7'h50 ;
|
|
'hEA: orgBase = 7'h51 ;
|
|
'hEB: orgBase = 7'h51 ;
|
|
'hF0: orgBase = 7'h52 ;
|
|
'hF1: orgBase = 7'h52 ;
|
|
'hF2: orgBase = 7'h52 ;
|
|
'hF3: orgBase = 7'h52 ;
|
|
'hF8: orgBase = 7'h53 ;
|
|
'hF9: orgBase = 7'h53 ;
|
|
'hFA: orgBase = 7'h53 ;
|
|
'hFB: orgBase = 7'h53 ;
|
|
|
|
default:
|
|
orgBase = 'X;
|
|
endcase
|
|
end
|
|
|
|
endmodule
|
|
|
|
//
|
|
// For compilation test only
|
|
//
|
|
|
|
`ifdef FX68K_TEST
|
|
module fx68kTop( input clk32,
|
|
input extReset,
|
|
// input pwrUp,
|
|
|
|
input DTACKn, input VPAn,
|
|
input BERRn,
|
|
input BRn, BGACKn,
|
|
input IPL0n, input IPL1n, input IPL2n,
|
|
input [15:0] iEdb,
|
|
|
|
output [15:0] oEdb,
|
|
output eRWn, output ASn, output LDSn, output UDSn,
|
|
output logic E, output VMAn,
|
|
output FC0, output FC1, output FC2,
|
|
output BGn,
|
|
output oRESETn, output oHALTEDn,
|
|
output [23:1] eab
|
|
);
|
|
|
|
// Clock must be at least twice the desired frequency. A 32 MHz clock means a maximum 16 MHz effective frequency.
|
|
// In this example we divide the clock by 4. Resulting on an effective processor running at 8 MHz.
|
|
|
|
reg [1:0] clkDivisor = '0;
|
|
always @( posedge clk32) begin
|
|
clkDivisor <= clkDivisor + 1'b1;
|
|
end
|
|
|
|
/*
|
|
These two signals must be a single cycle pulse. They don't need to be registered.
|
|
Same signal can't be asserted twice in a row. Other than that there are no restrictions.
|
|
There can be any number of cycles, or none, even variable non constant cycles, between each pulse.
|
|
*/
|
|
|
|
wire enPhi1 = (clkDivisor == 2'b11);
|
|
wire enPhi2 = (clkDivisor == 2'b01);
|
|
|
|
|
|
fx68k fx68k( .clk( clk32),
|
|
.extReset, .pwrUp( extReset), .enPhi1, .enPhi2,
|
|
|
|
.DTACKn, .VPAn, .BERRn, .BRn, .BGACKn,
|
|
.IPL0n, .IPL1n, .IPL2n,
|
|
.iEdb,
|
|
|
|
.oEdb,
|
|
.eRWn, .ASn, .LDSn, .UDSn,
|
|
.E, .VMAn,
|
|
.FC0, .FC1, .FC2,
|
|
.BGn,
|
|
.oRESETn, .oHALTEDn, .eab);
|
|
|
|
endmodule
|
|
`endif
|