diff options
author | H. Peter Anvin <hpa@zytor.com> | 2003-09-12 04:42:55 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2003-09-12 04:42:55 +0000 |
commit | 7a4493a8ebd6633f0f892cdb4a035c7d348f8392 (patch) | |
tree | fdebdfd540c5e470821aa712aa9e90d107097b21 /t80pio | |
parent | 04ed1bd635b5566f115cde9ab44204337149f406 (diff) | |
download | abc80-7a4493a8ebd6633f0f892cdb4a035c7d348f8392.tar.gz abc80-7a4493a8ebd6633f0f892cdb4a035c7d348f8392.tar.xz abc80-7a4493a8ebd6633f0f892cdb4a035c7d348f8392.zip |
*** empty log message ***
Diffstat (limited to 't80pio')
-rw-r--r-- | t80pio/README | 32 | ||||
-rw-r--r-- | t80pio/t80pio.v | 228 |
2 files changed, 260 insertions, 0 deletions
diff --git a/t80pio/README b/t80pio/README new file mode 100644 index 0000000..f5f012e --- /dev/null +++ b/t80pio/README @@ -0,0 +1,32 @@ +This is a peripheral device modelled after Zilog's Z80PIO. It +diverges from the Z80PIO in the following ways, in order to make it +better suited for embedded implementation without the constraints of +physical pin count: + +- The Z80PIO is a dual-port device, which in some conditions "borrow" + pins (BSTB# and BRDY are sometimes used by port A), but otherwise + the two ports are completely independent. This implementations + contains only one port; however, instantiating two to create a + two-port module is trivial; including the pin-sharing behaviour of a + Z80PIO (connect BSTB# to both A.BSTB_n and B.ASTB_n; + assign BRDY = A.BRDY | B.ARDY). + +- The pin names are consistent with the "A" port of the Z80PIO. For + the bidirectional busses D7..D0 and A7..A0 I have separated out the + input and output sides as DI/DO and AI/AO since embedded + applications usually do not use multidriver tristate busses. The + suffix _n is used for inverted signals (since neither Verilog nor + VHDL allows # in symbol names.) + +- The Z80PIO snoops the CPU data bus to recognize the RETI + instruction. Since this implementation doesn't use true shared + busses, an additional input, RETI#, has been added; this is expected + to be driven low by the CPU (or by additional logic plugged into the + CPU input data bus) when executing the RETI instruction. + +- The reset is not multiplexed onto the M1# pin like it is on the + Z80PIO. Instead, a RESET# input is provided. + +- The output data bus (Do) is set to all ones at any time it wouldn't + be driven by Z80PIO. The intent is that all the data outputs can be + AND'd together before sending to the CPU. diff --git a/t80pio/t80pio.v b/t80pio/t80pio.v new file mode 100644 index 0000000..f2f6e7c --- /dev/null +++ b/t80pio/t80pio.v @@ -0,0 +1,228 @@ +module T80PIO ( + CLK_n, + RST_n, + + Di, + Do, + CDsel, /* C/D# sel */ + CE_n, + M1_n, + IORQ_n, + RD_n, + IEI, + IEO, + INT_n, + + Ai, + Ao, + ASTB_n, + ARDY, + BSTB_n, + BRDY + ); + input CLK_n; + input RST_n; + + input [7:0] Di; + output [7:0] Do; + input CDsel; + input CE_n; + input M1_n; + input IORQ_n; + input RD_n; + input IEI; + input IEO; + input INT_n; + input [7:0] Ai; + input [7:0] Ao; + input ASTB_n; + output ARDY; + input BSTB_n; + output BRDY; + + reg [7:1] V; // Interrupt vector + reg [1:0] M; // Operating mode + reg [7:0] IO; // Mode 3 direction mask + reg [3:0] ICW; // Interrupt control word + reg [7:0] MB; // Interrupt mask + reg [7:0] D; // Output direction data register + reg [7:0] Do_q; // Data out to CPU + + reg io_next; // Next control word is I/O mask + reg mb_next; // Next control word is interrupt mask + reg i_data_flag; // Have input data + reg o_data_flag; // Have input data + reg ASTB_n_old; // Previous ASTB_n + + wire [7:0] data; // "Current data" + reg [7:0] data_in; // Latched input data (Di) + + // Data as it would be read by the CPU + assign read_data = + (M == 2'b00) ? D : + (M == 2'b01) ? data_in : + (M == 2'b10) ? data_in : + (M == 2'b11) ? (D & ~IO) | (data_in & IO); + + assign Do = Do_q; + + // Output data word (note: pins which would be tristated on Z80PIO + // we feed back the input.) + assign Ao = + (M == 2'b00) ? D : + (M == 2'b01) ? data_in : + (M == 2'b10) ? D : + (M == 2'b11) ? (D & ~IO) | (data_in & IO); + + // Output RDY signals + assign ARDY = + (M[0] == 1'b0) ? o_data_flag : + (M == 2'b01) ? ~i_data_flag : + 0; + assign BRDY = (M == 2'b10) && ~i_data_flag; + + // This signal is high if we should latch the input data + assign latch_in = + ((M == 2'b01 && ~ASTB_n) | + (M == 2'b10 && ~BSTB_n) | + (M == 2'b11)) & + ~( ~cpu_iorq_n & ~cpu_ce_n ); + + // Output INT# + reg need_irq; // Need to issue interrupt + reg intak; // INTAK in process + reg servicing_irq; // Waiting for RETI + + assign INT_n = ~( need_irq & IEI ); + assign IEO = IEI & ~need_irq & ~intak & ~servicing_irq; + + always @(posedge CLK_n or negedge RST_n) + begin + if ( !RST_n ) + begin + M <= 2'b01; + IO <= 0; + ICW <= 0; + MB <= 0; + D <= 0; + io_next <= 0; + mb_next <= 0; + need_irq <= 0; + intak <= 0; + servicing_irq <= 0; + ASTB_n_old <= ASTB_n; + Do_q <= ~0; // Output all ones when "inactive" + end + else + begin + Do_q <= ~0; + + if ( M1_n & ~IORQ_n & ~CE_n ) + begin + case ( { RD_n, CDsel } ) + 2'b00: // Data Read + begin + i_data_flag <= 0; + Do_q <= read_data; + end + + 2'b01: // Control read + begin + // No readable control registers + end + + 2'b10: // Data Write + begin + D <= Di; + o_data_flag <= 1; + end + + 2'b11: // Control Write + begin + io_next <= 0; + mb_next <= 0; + + if ( io_next ) + IO <= Di; + else if ( mb_next ) + MB <= Di + else + begin + casex ( Di[3:0] ) + 4'bxxx0: + V[7:1] <= Di[7:1]; + 4'b1111: + begin + M[1:0] <= Di[7:6]; + io_next <= (Di[7:6] == 2'b11); + end + 4'b0111: + begin + ICW[3:0] <= Di[7:4]; + mb_next <= Di[4]; + end + 4'b0011: + begin + ICW[3] <= Di[7]; + end + endcase // casex( Di[3:0] ) + end // else: !if( mb_next ) + end // case: 2'b11 + endcase // case( { RD_n, CDsel } ) + end // if ( ~IORQ_n & ~CE_n ) + else + // Latch input data every cycle unless we are + // currently being addressed by the CPU + if ( latch_in ) + data_in <= Di; + + // Input strobe detect + if ( ASTB_n & ~ASTB_n_old ) // ASTB_n rising edge + begin + if ( M[0] == 1'b0 ) // Output or Bidir modes + o_data_flag <= 0; // Data sent, now empty + else if ( M == 2'b01 ) // Input mode + i_data_flag <= 1; // Data received, now full + + if ( M[0] != 2'b11 && ICW[3] ) + need_irq <= 1; + end + + // Delayed ASTB# for edge detection + ASTB_n_old <= ASTB_n; + + // Mode 3 interrupt control + if ( M == 2'b11 && ICW[3] ) + begin + wire [7:0] xor_mask = ICW[1] ? 8'hFF : 8'h00; + + wire irq_cond = ICW[2] ? + // "AND" mode + !((read_data ^ xor_mask) & MB) : + // "OR" mode + ((read_data ^ ~xor_mask) & MB); + + need_irq <= need_irq | irq_cond; + end // if ( M == 2'b11 && ICW[3] ) + + // Actual interrupt-generating logic. We have two + // possible states to be in; both can be active: + // need_irq (INT# -> INTAK) and servicing_irq (INTAK -> RETI). + if ( ~M1_n & ~IORQ_n & ((need_irq & IEI) | intak) ) + begin + Do_q <= { V, 1'b0 }; + servicing_irq <= 1; + intak <= 1; + need_irq <= 0; + end + else + intak <= 0; + + if ( ~RETI_n ) + servicing_irq <= 0; + + end // else: !if( !RST_n ) + end // always @ (posedge CLK_n or negedge RST_n) +endmodule // T80PIO + +
\ No newline at end of file |