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.

  1 // This CPLD meant for the Lattice M4A5 64/32 is a simple bank switching
  2 // memory control unit. It has 8 registers:
  3 //  00 - bank 0
  4 //  01 - bank 1
  5 //  02 - bank 2
  6 //  03 - bank 3
  7 //  04 - update 0
  8 //  05 - update 1
  9 //  06 - update 2
 10 //  07 - update 3
 11 //
 12 //  The banks are meant to substitute starting address line 14.
 13 //  The update registers can only be written to from bank 00 i.e. the lowest
 14 //  16kb.
 15 //
 16 //  The banks are updated from the update registers when HALT is called
 17 //  from bank 0. NMI immediately follows HALT in this circumstance
 18 //
 19 
 20 `define BANKTOP 4
 21 `define IO_RANGE 7:3
 22 `define IO_VALUE 5'b00000
 23 `define ACTIVELOW 1'b0
 24 
 25 module mcu (
 26   input pin_CLK,
 27   input [15:0] pins_A,
 28   input pin_nRESET, pin_nWR, pin_nRD, pin_nMREQ, pin_nIORQ, pin_nM1, pin_nHALT,
 29   output reg pin_nNMI,
 30   output [`BANKTOP-1:0] pins_Aout,
 31   output pin_nCS0, pin_nCS1,
 32   inout [7:0] pins_D
 33 );
 34 
 35   reg [`BANKTOP:0] bankReg0;
 36   reg [`BANKTOP:0] bankReg1;
 37   reg [`BANKTOP:0] bankReg2;
 38   reg [`BANKTOP:0] bankReg3;
 39   reg [`BANKTOP:0] updateReg0;
 40   reg [`BANKTOP:0] updateReg1;
 41   reg [`BANKTOP:0] updateReg2;
 42   reg [`BANKTOP:0] updateReg3;
 43   reg nKernel;
 44   wire nInRange;
 45   reg [7:0] Dint;
 46   wire [`BANKTOP:0] selBank;
 47 
 48   // Determine if the address bus is talking to us
 49   assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE) ? 1'b0 : 1'b1) | pin_nIORQ;
 50 
 51   // Get selected output
 52   assign selBank = pins_A[15] ? (pins_A[14] ? bankReg3 : bankReg2) : (pins_A[14] ? bankReg1 : bankReg0);
 53 
 54   // assign Aout -- Hi-Z on reset
 55   assign pins_Aout = pin_nRESET ? selBank[`BANKTOP-1:0] : 8'bzzzzzzzz;
 56 
 57   // CS's for top bit of bank
 58   assign pin_nCS0 = pin_nRESET ? (selBank[`BANKTOP] | pin_nMREQ) : 1'bz;
 59   assign pin_nCS1 = pin_nRESET ? ((~selBank[`BANKTOP]) | pin_nMREQ) : 1'bz;
 60 
 61   assign pins_D = ((pin_nRD | nInRange) == 1'b0) ? Dint : 8'bzzzzzzzz;
 62 
 63   always @(*) begin
 64     case (pins_A[2:0])
 65       3'b000:
 66         Dint = bankReg0;
 67       3'b001:
 68         Dint = bankReg1;
 69       3'b010:
 70         Dint = bankReg2;
 71       3'b011:
 72         Dint = bankReg3;
 73       3'b100:
 74         Dint = updateReg0;
 75       3'b101:
 76         Dint = updateReg1;
 77       3'b110:
 78         Dint = updateReg2;
 79       3'b111:
 80         Dint = updateReg3;
 81     endcase
 82   end
 83 
 84   always @(posedge pin_CLK) begin
 85     if (pin_nRESET == `ACTIVELOW) begin
 86       bankReg0 <= 0;
 87       bankReg1 <= 1;
 88       bankReg2 <= (1 << `BANKTOP);
 89       bankReg3 <= (1 << `BANKTOP) | 1;
 90       pin_nNMI <= 1'b1;
 91     end
 92     else begin
 93       if ((pin_nHALT | nKernel) == `ACTIVELOW) begin
 94         bankReg0 <= updateReg0;
 95         bankReg1 <= updateReg1;
 96         bankReg2 <= updateReg2;
 97         bankReg3 <= updateReg3;
 98       end
 99 
100       pin_nNMI <= pin_nHALT;
101       
102       if ((pin_nM1 | pin_nMREQ) == `ACTIVELOW) begin
103         nKernel <= pins_A[15] | pins_A[14];
104       end
105     end
106   end
107   
108   always @(posedge pin_nWR) begin
109     if (((pin_nIORQ | nInRange) == `ACTIVELOW) && (pins_A[2] == 1'b1)) begin
110       case (pins_A[1:0])
111         2'b00: begin
112           updateReg0 <= pins_D;
113         end
114         2'b01: begin
115           updateReg1 <= pins_D;
116         end
117         2'b10: begin
118           updateReg2 <= pins_D;
119         end
120         2'b11: begin
121           updateReg3 <= pins_D;
122         end
123       endcase
124     end
125   end
126 
127 endmodule

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