Dr Jefyll wrote:
The complication is that Phi2 is usually what's used to qualify the /WE pulse that's sent to RAM and to non-65xx peripherals. And that's fine for devices that don't need extra time, because you want the rise of Phi2 to cause /WE to go low without delay. But in the case of a device that
does need extra time, there could be trouble ("rogue" writes to incorrect addresses) if Phi2 causes /WE to go low without delay.
This seems to imply that by the time Phi2 rises we need to know whether or not extra time will be required. And, in an overclocked system, knowing this by the time Phi2 rises is a problem.
Okay, so this morning I'm doing a head slap.
The problem goes away if devices that need extra time
don't use the same /WE pulse that's sent to fast devices. A little extra logic can give slow devices a /WE pulse of their own -- it would be a fairly minor addition to the clock-stretch circuit. Forgive me if that's what you already had in mind, kernelthread.
-- Jeff
To be honest, I hadn't realised this might be a problem. I was just considering a system consisting of RAM, ROM and VIA(s). RAM WE can be qualified with PHI2 as usual, as can WE for Flash memory if that is being used for ROM. VIAs are a problem because the chip select has to be asserted before the VIA input clock rises. But then you also don't really want to use PHI2 as the VIA input clock because the timers will be affected by clock stretching - you ideally want a constant clock input to the VIA so you can get predictable timing intervals.
I've come up with some Verilog code (not tested) to do what I was thinking of:
Code:
input FCLK; // fast clock, 2 x nominal CPU frequency
input nRST; // system reset signal
reg [2:0] STATE; // Clock stretch state (see below)
reg PHI2; // clock to CPU
reg VIA_CLK; // clock for VIAs and similar peripherals (half nominal CPU frequency, constant frequency)
reg VIA_SEL; // chip select for VIAs
// asserted when VIA_CLK is low when VIA access is needed
// deasserted at following VIA_CLK falling edge
reg DELAYED_SEL; // delayed chip select for any other purpose
// asserted at beginning of first stretch cycle
// deasserted at end of bus cycle
// maybe useful if any device requires more settling time before RD/WR strobes activated
/* STATE values:
000 First half cycle of every bus cycle, PHI2 = 0
001 Second half cycle of every bus cycle, PHI2 = 1. Last half cycle if no stretching needed.
010 6 input clocks (3 nominal CPU cycles) remaining, PHI2 = 1
011 5 input clocks (2.5 nominal CPU cycles) remaining, PHI2 = 1
100 4 input clocks (2 nominal CPU cycles) remaining, PHI2 = 1
101 3 input clocks (1.5 nominal CPU cycles) remaining, PHI2 = 1
110 2 input clocks (1 nominal CPU cycles remaining, PHI2 = 1
111 Last half cycle following stretch, PHI2 = 1
*/
// These logic terms should be capable of evaluation within one clock period of FCLK
// It is assumed they are functions of the active address, so remain asserted for the entire bus cycle
wire STRETCH1 = ... ; // insert logic to determine if device needs stretch by 1 cycle
wire STRETCH2 = ... ; // insert logic to determine if device needs stretch by 2 cycles
wire STRETCH3 = ... ; // insert logic to determine if device needs stretch by 3 cycles
wire IS_VIA = ... ; // insert logic to determine if it's a VIA
always @(posedge FCLK or negedge nRST) begin
if (!nRST) begin
STATE <= 3'b000;
PHI2 <= 1'b0;
VIA_CLK <= 1'b0;
VIA_SEL <= 1'b0;
DELAYED_SEL <= 1'b0;
end
else if (STATE == 3'b000) begin
// CPU clock is low in state 000, high in any other state
STATE <= 3'b001;
PHI2 <= 1'b1;
end
else if (STATE == 3'b001) begin
// Second state of bus cycle - need to determine required wait states
// by the end of this state.
if (STRETCH1) begin
// state increments twice before PHI2 goes low again
STATE <= 3'b110;
// delayed select asserted at beginning of stretch
DELAYED_SEL <= 1'b1;
end
else if (STRETCH2) begin
// state increments 4 times before PHI2 goes low again
STATE <= 3'b100;
// delayed select asserted at beginning of stretch
DELAYED_SEL <= 1'b1;
end
else if (STRETCH3) begin
// state increments 6 times before PHI2 goes low again
STATE <= 3'b010;
// delayed select asserted at beginning of stretch
DELAYED_SEL <= 1'b1;
end
else if (IS_VIA) begin
// VIA_CLK will toggle at the next input clock, however it is not
// known whether it will go low or high
// If it will go low, assert VIA_SEL at the same time and do a 2 cycle stretch
// If it will go high, don't assert VIA_SEL yet, but start a 3 cycle stretch
// VIA_SEL will be asserted after 1 cycle of stretch
if (VIA_CLK) begin
// VIA_CLK will go low at next input clock, so assert VIA_SEL there as well
VIA_SEL <= 1'b1;
// need 2 cycle stretch
STATE <= 3'b100;
end
else begin
// need 3 cycle stretch, don't assert VIA_SEL yet
STATE <= 3'b010;
end
end
else begin
// no stretching needed - back to PHI2=0
STATE <= 3'b000;
PHI2 <= 1'b0;
end
end
else if (STATE == 3'b111) begin
// Last state of bus cycle following stretching
// Back to PHI2=0 on next input clock
STATE <= 3'b000;
PHI2 <= 1'b0;
// VIA select deasserted here
VIA_SEL <= 1'b0;
// delayed select deasserted here
DELAYED_SEL <= 1'b0;
end
else begin
// stretching operation underway but not yet at the end
// states 010, 011, 100, 101, 110
if ((STATE == 3'b011) & IS_VIA) begin
// VIA_CLK will go low at next input clock, so assert VIA_SEL there as well
VIA_SEL <= 1'b1;
end
STATE <= STATE + 3'b001;
end
if (STATE[0]) begin
// VIA_CLK toggles whenever unstretched clock would fall
// i.e. when state number transitions from odd to even
VIA_CLK <= ~VIA_CLK;
end
end
I added a DELAYED_SEL signal to accommodate any devices which need additional settling time for the address and/or data before being activated, following your suggestion.