Wednesday, July 29, 2015

MCU Verilog

Since I'm starting to get ready to mount the MCU CPLD, I thought I'd post the Verilog. It's pretty straightforward.

I made an error in my assumption of the flash programmer, though and I see I have to possibly change something. I was assuming that the flash device was using the rising edge of the WR line to write and starting to drive the data lines at the same as WR went low. This led to errors because it was actually using the falling edge of WR and so the data wasn't properly set up.

According to the Z-80 data sheet, the data is set up and held for ~15 ns before and after the WR line low is low. So, I should be able to use the rising edge of WR without a problem.

       
// This CPLD meant for the Lattice M4A5 64/32 is a simple bank switching
// memory control unit. It has 8 registers:
//  00 - bank 0
//  01 - bank 1
//  02 - bank 2
//  03 - bank 3
//  04 - update 0
//  05 - update 1
//  06 - update 2
//  07 - update 3
//
//  The banks are meant to substitute starting address line 14.
//  The update registers can only be written to from bank 00 i.e. the lowest
//  16kb.
//
//  The banks are updated from the update registers when HALT is called
//  from bank 0. NMI immediately follows HALT in this circumstance
//

`define BANKTOP 4
`define IO_RANGE 7:3
`define IO_VALUE 5'b00000
`define ACTIVELOW 1'b0

module mcu (
    input pin_CLK,
    input [15:0] pins_A,
    input pin_nRESET, pin_nWR, pin_nRD, pin_nMREQ, pin_nIORQ, pin_nM1, pin_nHALT,
    output reg pin_nNMI,
    output [`BANKTOP-1:0] pins_Aout,
    output pin_nCS0, pin_nCS1,
    inout [7:0] pins_D
);

    reg [`BANKTOP:0] bankReg0;
    reg [`BANKTOP:0] bankReg1;
    reg [`BANKTOP:0] bankReg2;
    reg [`BANKTOP:0] bankReg3;
    reg [`BANKTOP:0] updateReg0;
    reg [`BANKTOP:0] updateReg1;
    reg [`BANKTOP:0] updateReg2;
    reg [`BANKTOP:0] updateReg3;
    reg nKernel;
    wire nInRange;
    reg [7:0] Dint;
    wire [`BANKTOP:0] selBank;

    // Determine if the address bus is talking to us
    assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE) ? 1'b0 : 1'b1) | pin_nIORQ;

    // Get selected output
    assign selBank = pins_A[15] ? (pins_A[14] ? bankReg3 : bankReg2) : (pins_A[14] ? bankReg1 : bankReg0);

    // assign Aout -- Hi-Z on reset
    assign pins_Aout = pin_nRESET ? selBank[`BANKTOP-1:0] : 8'bzzzzzzzz;

    // CS's for top bit of bank
    assign pin_nCS0 = pin_nRESET ? (selBank[`BANKTOP] | pin_nMREQ) : 1'bz;
    assign pin_nCS1 = pin_nRESET ? ((~selBank[`BANKTOP]) | pin_nMREQ) : 1'bz;

    assign pins_D = ((pin_nRD | nInRange) == 1'b0) ? Dint : 8'bzzzzzzzz;

    always @(*) begin
        case (pins_A[2:0])
            3'b000:
                Dint = bankReg0;
            3'b001:
                Dint = bankReg1;
            3'b010:
                Dint = bankReg2;
            3'b011:
                Dint = bankReg3;
            3'b100:
                Dint = updateReg0;
            3'b101:
                Dint = updateReg1;
            3'b110:
                Dint = updateReg2;
            3'b111:
                Dint = updateReg3;
        endcase
    end

    always @(posedge pin_CLK) begin
        if (pin_nRESET == `ACTIVELOW) begin
            bankReg0 <= 0;
            bankReg1 <= 1;
            bankReg2 <= (1 << `BANKTOP);
            bankReg3 <= (1 << `BANKTOP) | 1;
            pin_nNMI <= 1'b1;
        end
        else begin
            if ((pin_nHALT | nKernel) == `ACTIVELOW) begin
                bankReg0 <= updateReg0;
                bankReg1 <= updateReg1;
                bankReg2 <= updateReg2;
                bankReg3 <= updateReg3;
            end

            pin_nNMI <= pin_nHALT;
           
            if ((pin_nM1 | pin_nMREQ) == `ACTIVELOW) begin
                nKernel <= pins_A[15] | pins_A[14];
            end
        end
    end
   
    always @(posedge pin_nWR) begin
        if (((pin_nIORQ | nInRange) == `ACTIVELOW) && (pins_A[2] == 1'b1)) begin
            case (pins_A[1:0])
                2'b00: begin
                    updateReg0 <= pins_D;
                end
                2'b01: begin
                    updateReg1 <= pins_D;
                end
                2'b10: begin
                    updateReg2 <= pins_D;
                end
                2'b11: begin
                    updateReg3 <= pins_D;
                end
            endcase
        end
    end

endmodule

So, this is already old. I added a nBUSQACK pin for better cooperation with the in circuit flash programmer. Also added it to the testbench. It seems to work in the simulation.

No comments:

Post a Comment