Not so much luck with PVB5. ISE sees the PROMs but no action from the board. I tried to modify my SOP for soldering... Will figure it out later.
Had some luck re-routing some signals to center 4 outputs horizontally. The last output board PVB6 now outputs its primary pixel clock back through to the PVB5's videoDAC and pixel clock in. Then PVB5 countflag is delayed some cycles using a 1-bit shift register. Countflag triggers 1 cycle after hstart. Same concept for PVB5 into PVB4, etc. Now resetting any board (except PVB6) does not take out the whole picture.
Before, PVB6 was the output board but PVB3 was the master pixel clock generator passing it's pixel clock through it's videoDAC onto PVB4, etc.
Now PVB6 is still the output board but outputs its pixel clock to its videoDAC and PVB5's videoDAC (used to be pixel clock out, now pixel clock in). Still working on the block diag!
PVB6 no delay. Output board. 6.5ns SyncRAM.
For PVB5 a countflag delay of 6. 6.5ns SyncRAM.
For PVB4 a countflag delay of 15. 4.0ns SyncRAM.
For PVB3 a countflag delay of 23. 4.0ns SyncRAM.
The next challenge is to delay vertically. I've not had much luck there yet for some reason.
Here is my modified version of Arlet's vga.v controller for PVB3.
Code:
module vga( input clk,
input [10:0] OACCout, // vertical offset register
input [10:0] NACCout, // horizontal offset register
input CB0, // 1 = bank 1, 0 = bank 0
input CB1, // 1 = scroll, 0 = bank switch
input CB2, // 1 = horizontal scroll, 0 = vertical scroll
input CB3, // 1 = arrange memory for vertical scroll and allow offscreen plotting in Y direction
output reg hsync, // horizontal sync out
output reg vsync, // vertical sync out
output reg hstart, // line start on next clock
output reg vstart, // field starts on next clock
output reg hblank = 1, // hblank on next clock
output reg vblank = 0, // vblank on next clock
output reg [20:0] Vaddr,// to SyncRAM
output reg countflag, // to VDACdata module
input HSYNCin, // horizontal sync input
input VSYNCin // vertical sync input
);
// timing parameters
parameter
HFRONT = 3, // front porch (+right border)
HVIDEO = 1024, // active video area
HSYNC = 3, // hsync
HBACK = 316; // back porch (+left border) :1346 horizontal pixels total 356
parameter
VFRONT = 3, // front porch (+bottom border)
VVIDEO = 768, // active video area
VSYNC = 6, // vsync
VBACK = 30; // back porch (+top border)
// states (used for both H and V state machines)
parameter
VIDEO = 2'd0,
FRONT = 2'd1,
SYNC = 2'd2,
BACK = 2'd3;
// horizontal counters and state
reg [10:0] hcount = 0; // down counter for horizontal state
reg [9:0] hload; // next load value
reg [1:0] hstate = FRONT; // horizontal state
wire hcount_done = hcount[10]; // done when count < 0
// vertical counters and state
reg [10:0] vcount = 0; // down counter for vertical state
reg [9:0] vload; // next load value
reg [1:0] vstate = FRONT; // horizontal state
wire vcount_done = vcount[10]; // done when count < 0
// enable signal for V state machine
wire venable = (hstate == FRONT) & hcount_done;
// preload next timer value depending on state
// subtract 2 from timing value to compensate for pipeline delays
// This should be synthesized as a small LUT ROM, so it's quite
// efficient.
always @(posedge clk)
hload <= (hstate == BACK) ? HVIDEO - 2:
(hstate == VIDEO) ? HFRONT - 2:
(hstate == FRONT) ? HSYNC - 2:
HBACK - 2;
// do the same for vertical
always @(posedge clk)
vload <= (vstate == BACK) ? VVIDEO - 2:
(vstate == VIDEO) ? VFRONT - 2:
(vstate == FRONT) ? VSYNC - 2:
VBACK - 2;
// adjust down counter. Reload when it's done.
always @(posedge clk)
if( HSYNCin | hcount_done )
hcount <= { 1'b0, hload };
else
hcount <= hcount - 1;
// when down counter is done, go to next state
always @(posedge clk)
if( hcount_done )
case( hstate )
VIDEO : hstate <= FRONT;
FRONT : hstate <= SYNC;
SYNC : hstate <= BACK;
BACK : hstate <= VIDEO;
endcase
else if (HSYNCin)
hstate <= VIDEO;
// start of active line
always @(posedge clk)
hstart <= (hstate == BACK) & hcount_done & ~vblank;
// vstart pulse to signal end of vertical blanking
always @(posedge clk)
vstart <= (vstate == BACK) & venable & vcount_done;
// adjust down counter. Reload when it's done.
always @(posedge clk)
if( venable ) begin
if( VSYNCin | vcount_done )
vcount <= { 1'b0, vload };
else
vcount <= vcount - 1;
end
// when down counter is done, go to next state
always @(posedge clk)
if( venable & vcount_done )
case( vstate )
VIDEO : vstate <= FRONT;
FRONT : vstate <= SYNC;
SYNC : vstate <= BACK;
BACK : vstate <= VIDEO;
endcase
else if (VSYNCin)
vstate <= VIDEO;
// update registered outputs
always @(posedge clk) begin
hsync <= hstate == SYNC;
vsync <= vstate == SYNC;
hblank <= (vstate == VIDEO) & (hstate == VIDEO) & hcount_done;
vblank <= vstate != VIDEO;
end
//delay countflag using a single bit shift register
parameter hvd = 23;
reg [hvd:0] s;
wire q = s[hvd];
always @(posedge clk)
s <= { s[hvd-1:0], countflag };
//pixel counters & video address
always @(posedge clk)
if ( hstart )
countflag <= 1;
else if ( hblank )
countflag <= 0;
//X & Y pixel counters
reg [9:0] X = 0;
reg [10:0] Y = 0;
always @(posedge clk) begin
if ( vstart | ( X == 1023 & Y == 767 )) begin
X <= 0;
Y <= 0;
end
if ( X == 1023 ) begin
Y <= Y + 1;
X <= 0;
end
else if ( q )
X <= X + 1;
end
//X & Y addressing using pixel counters
always @*
if (!CB3) begin //CB3 = 1 to allow offscreen plotting in Y direction
if (CB1) begin //CB1 = 1 to scroll, 0 to bank switch
if (CB2) Vaddr [20:0] <= { X + NACCout, 10'd768 - Y [9:0]}; //CB2 = 1 to scroll horizontal
else Vaddr [20:0] <= { Y [9:0] + OACCout, X }; //scrolling vertical
end
else Vaddr [20:0] <= { CB0, Y [9:0], X }; //bank switching
end
else Vaddr [20:0] <= { Y, X };
endmodule