ULA revistited.

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
sirmorris
Posts: 2811
Joined: Thu May 08, 2008 5:45 pm

Re: ULA revistited.

Post by sirmorris »

I love it when I have no idea what you're talking about! PHWAORRR! <rubs trousers>
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

sirmorris wrote:I love it when I have no idea what you're talking about! PHWAORRR! <rubs trousers>
Prevert!

You could always take the plunge, get some CPLDs and learn to program them, cirtainly none of that tedious mucking about with remembering the polaritys of inputs / outputs as the (Xilinx's anyways) software works it all out for you, unlike CUPL.

If it would help I can give you the schematics / verilog code for the Atom RAMROM, and th ZXULA.....humm wonder if I could re-implement ZXPAND using a CPLD....prolly an XC9536 would do the trick.

Phill.
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

Andy Rea wrote:or simpler still...

Code: Select all

always @ (negedge clk325) begin

	//if (turboon & clk325) turbo <= 1'b1 ;
	//else if (turbooff & clk325) turbo <= 1'b0;

	if (turboon) turbo <= 1'b1 ;
	else if (turbooff) turbo <= 1'b0;
	
	else turbo <= turbo ;
end
since as the always block is triggered on the negedge of clk325 we know that clk65 has already gone low (a few ns previously) and in either case turbo / non turbo clkout will also be low too.

of course you need to wrap it up in the rest of the switching code :D
Ok, cheers Andy, I'll give that a go this evening and see how we get on.

That is of course asuming that my Z80B will run at 6.5MHz, if not I'm gonna order some faster ones from Farnell as they are only about a fiver each :)

Cheers.

Phill.
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

sirmorris wrote:I love it when I have no idea what you're talking about! PHWAORRR! <rubs trousers>
:lol: it's not that hard really.... you can learn verilog without ever touching a CPLD, xlinx, altera, lattice, all have free software, mostly including simulation software that also takes device timings into consideration, useful for finding those glitches ;)

i picked verilog, because it seemed to have a fair amount of information on the web about it, i first looked at altera's own HDL becuase it looked similar to cupl, but couldn't find much info about it, but then again verilog is accepted by most compilers anyway.


Regards Andy
what's that Smell.... smells like fresh flux and solder fumes...
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

Image

Image

built minus 3 resistors, tape-in and the video-csync, the tape-in will need tweaking i reckon, and since as the video and csync are now 3.3v outputs that will probably want tweaking too.

pity i can't program the thing yet, still waiting on an ebay purchase :lol:

no idea if it will work :? but i did make a schoolboy error on the pcb design... both transoistors are back to front :oops: so now they don't match the silk screen good job the 3.3v regulator was right... but i did check that before powering up, with the transistors the right way round all i can tell you is that the oscillator is working.

should of made those 4 pads at the bottom bigger, but they'll do, only for solderng the jtag wires to, and i'll pick up gnd and 3.3v at the regulator :lol:

Oh hurry up chinese post....

Regards Andy
what's that Smell.... smells like fresh flux and solder fumes...
sirmorris
Posts: 2811
Joined: Thu May 08, 2008 5:45 pm

Re: ULA revistited.

Post by sirmorris »

WHAAAAAT?!

Woooooooowoooo!

Lookin' good, Andy!

How are you mounting the pins? It looks like you have pads on the underside only.
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

sirmorris wrote: How are you mounting the pins? It looks like you have pads on the underside only.
Erm yeah, just pads on the underside, the CPLD is just a touch too wide to allow through pins, i could do with making the pads a little wider, but it shouldn't be a problem as it's not exactly a part thats going to be in and out all the time.
what's that Smell.... smells like fresh flux and solder fumes...
Prime
Posts: 134
Joined: Thu Mar 24, 2011 3:02 am

Re: ULA revistited.

Post by Prime »

Andy Rea wrote:
sirmorris wrote: How are you mounting the pins? It looks like you have pads on the underside only.
Erm yeah, just pads on the underside, the CPLD is just a touch too wide to allow through pins, i could do with making the pads a little wider, but it shouldn't be a problem as it's not exactly a part thats going to be in and out all the time.
Looking good, you could of course move the CPLD south a bit, I'm asuming that it's at the bottom end of the board....that way your pins will be able to be through the board, meaning the board will sit lower in the machine, should still fit how it is.

Depending on what manufacturer the CPLD is you may find that you have problems if it's a 3.3V but 5.0V tollerent chip I made a second prototype with a xilinx XC9572XL and while it works it got really hot and I was only able to program it once, I have not yet had a chance to try and remove it from the board and try mounting another chip so I'm not sure what happened, could be that I accidentally spiked one of the programming pins :(

Finally feel free to rip bist of my Verilog code if you want / need as it might help you get going quicker.

Cheers.

Phill.
User avatar
Andy Rea
Posts: 1606
Joined: Fri May 09, 2008 2:48 pm
Location: Planet Earth
Contact:

Re: ULA revistited.

Post by Andy Rea »

few more pictures...

Image
Image
Image

That Gal is not soldered in yet, i'll make sure i get the program right first :lol:

cheap skate is my middle name... the parts (not counting the PCB) cost just over £5-00 the most expensive been the cpld at £3-00, the gal does 2 things, 1) latches the databus ( char code grab ) and then puts that on the alternate address lines (bits 3 to 8) and also does the memory decoding (broken just like the original :D ) the idea with this bord is to not take up any more space than is really necessary, pity it overlaps the rom socket slightly could be a problem if peeps have extra gizmo's plugged in there, however the pads at the bottom can be cut off once the CPLD is programmed and working, it would still be a bit close....

Thought my parcel had turned up today as when i got home from work there was a post office card on the floor, but alas it was for the missus :?

And now something for the geeks... ( are we all geeky here anyway ??? )

my verilog code...

zx81ula TOP LEVEL CODE.

Code: Select all

Andy Rea march/april 2011

*/

// in sinclair fashion the device is not big enough for the job...
// so i use a gal16v8 to handle 6 of the 9 alternate address lines
// and also does the memory decoding
// this needs 2 pins, but relives the cpld of 10 pins
// making a saving of 8 pins in total

// this design is modular
// very little happens here.

// each module is a close approximation to the Gal design that i built
// and tested. they share the same name, for comparison.


module zx81ula (
//inputs
osc,				//oscilator input
read,				//z80 read sig
iorq,				//z80 io request
write,				//z80 write sig
m1,					//z80 m1 sig
halt,				//z80 halt sig
a15,				//z80 address line 15 (msb)
tape_in,			//tape input
kbd,				// 5 pins (kbd0 - kbd4)

//inouts
data_bus,			// z80 databus
add_low,			// a0, a1, a2, actually a2 is only an output...

//outputs
clock_out,			//3.25Mhz to Z80
nmi,				//non maskable int to z80
vid,				//video otput
sync,				//composite sync output
load_alt_address,	//to the gal
alt_address_en		//to the gal
);

//inputs
input osc;
input read;
input iorq;
input write;
input m1;
input halt;
input a15;
input tape_in;
input [4:0] kbd;

//inouts
inout [7:0] data_bus;
inout [2:0] add_low;

//outputs
output clock_out;
output nmi;
output vid;
output sync;
output load_alt_address;
output alt_address_en;

// register declarations
reg bit7;					//used in vshf to invert the video

// signal declarations
wire [2:0] alt_add;
wire blanking;				//used in vshf to add a back porch to the video signal
wire hsync;					//video sync signal
wire vsync;					//video sync signal
wire ferd;					//signal to read port $FE
wire force_nop;				//signal to force 8b'00000000 onto databus
wire enable_alt_address;	//signal to enable the alerternative addressing (for video data)
wire back_porch;			//back porch timer
wire load_vid;				//signal to load databus into vshf
wire csync;					//composite sync (generated with Hsync and Vsync)


// the logic, not much in this top_level module.

assign sync = !csync;							// the real csync needs to be inverted from whats used internally
assign load_alt_address = force_nop;			// 1 of 2 controls to the Gal
assign alt_address_en = enable_alt_address ;	// 2 of 2 controls to the Gal
assign add_low = (enable_alt_address ? alt_add : 3'bzzz ); // alternate address line control

always @ (posedge force_nop) bit7 <= data_bus [7];	//grab bit 7 used in module Vshf (invert bit)
	
	
// the following modules roughly follow my original Gal design

decode u0 (
//inputs
.clock (osc),
.a0 (add_low [0]),
.a1 (add_low [1]),
.rd (read),
.iorq (iorq),
.wr (write),
.hsync (hsync),

//outputs
.vsync (vsync),
.ferd (ferd),
.nmi (nmi),
.clock_out (clock_out)
);

nmi207 u1 (
//inputs
.clock3 (clock_out),
.reset (vsync),
//outputs
.bit4 (blanking),
.last16 (hsync)
);

lnc u2 (
//inputs
.clock (osc),    
.vsync (vsync),
.blank_time (blanking),
.hsync (hsync),
//outputs
.csync (csync),              
.back_porch (back_porch),
.alt_address (alt_add) 
);

vshf u3 (
//inputs
.clock (osc),
.data_in (data_bus),
.invert (bit7),
.blanking (back_porch),
.load_vshf (load_vid),
//outputs
.vid_out (vid)
);

state u4 (
//inputs
.clock6 (osc),
.m1 (m1),
.halt (halt),
.a15 (a15),
.d6 (data_bus [6]),
//outputs
.force_nop (force_nop),
.alt_address (enable_alt_address),
.load_vshf (load_vid)
);


//module ferd also controls the tri-state of the databus.
ferd u5 (
//inputs
.ferd (ferd),
.force_nop (force_nop),
.kbd (kbd),
.uk_us (1'b0),
.tape_in (tape_in),
//outputs
.data_out (data_bus)
);







endmodule
Ah ah got ya... it's modular, i did this so that i could stick close to my gal design logic, and each module can be test-benched and modified without affecting the other modules. the modules share the same names as my gal design.

Decode

Code: Select all

/*

ZX81 ULA implementation

Andy Rea march/april 2011

*/

// this module generates the 3.25Mhz clock
// contains the nmi latch
// contains the Vsync latch
// and generates ferd signal
// and nmi (to z80) signal

module decode (
// input ports
clock,
a0,
a1,
rd,
iorq,
wr,
hsync,

//output ports
vsync,
ferd,
nmi,
clock_out
);

input clock;		// 6.5Mhz clock from osc
input a0;			// z80 address line 0
input a1;			// z80 address line 1
input iorq;			// z80 io request
input wr;			// z80 write sig
input rd;			// z80 read sig
input hsync;		// hsync, used to generate the nmi pulses


output vsync;		//vsync
output ferd;		// read port $FE
output nmi;			//nmi pulse to z80
output clock_out;	//3.25Mhz clock to Z80


reg clock_out;
reg vsync;
reg nmi_latch;

wire nmi;		
wire ferd;

assign ferd = !(iorq | a0 | rd) ; 		//always generates a FERD signal when iorq, a0, rd are all low.
assign nmi = !(nmi_latch & hsync) ;		//generate an NMI pulse on every hsync if nmi_latch is set.


			

always @ (posedge clock)
	clock_out <= !clock_out;			//clock divider, 3.25Mhz from 6.5Mhz.


wire nmi_set = iorq | wr | a0 ;			// nmi_latch set/reset signals
wire nmi_reset = iorq | wr | a1 ;		// both produce an active low signal


// modified the following to make the latches syncronous

//always @ (negedge nmi_set or negedge nmi_reset )
always @ (posedge clock)
	begin
		if (!nmi_set) nmi_latch <= 1'b1 ; 		
		else if (!nmi_reset) nmi_latch <= 1'b0 ;
		else nmi_latch <= nmi_latch;
	end
	

	
wire io_wr = iorq | wr ;				//any io write will reset Vsync

//always @ (negedge io_wr or posedge ferd )
always @ (posedge clock)
	begin
		if (!io_wr) vsync <= 1'b0 ;				//any write reset Vsync
		else if (nmi_latch) vsync <= 1'b0 ;		//only latch if nmi_latch is reset.
		else if (ferd) vsync <= 1'b1 ;					
		else vsync <= vsync ;
	end
	
endmodule 
LNC (line counter)

Code: Select all

/*

ZX81 ULA implementation

Andy Rea march/april 2011

*/

// this module lnc (line counter) generates the lowest 3 address lines
// for the alternative addressing during video generation.
//
// the lowest 3 address is basically a 3 bit binary counter
// incremented on hsync pulses
// and reset (to zero) on vsync pulses
//
// this module also creates csync from hsync and vsync
//
// and generates a back porch timing pulse that starts
// on a vsync or hsync and continues whilst
// blank_time is low
//
// blank_time is bit4 from nmi207 

module lnc (
clock,    
vsync,
blank_time,
hsync,

csync,              
back_porch,
alt_address 
);

input clock;
input vsync;
input blank_time;
input hsync;

output csync;
output back_porch;
output [2:0] alt_address;

wire csync;
reg back_porch;
reg [2:0] alt_address;

assign csync = vsync | hsync;

always @ (posedge vsync or posedge hsync)
	begin
		if (vsync) alt_address <= 3'b000 ;
		else alt_address <= alt_address + 1'b1 ;
	end

always @ (posedge clock or posedge csync)
	begin
		if (csync) back_porch <= 1'b1 ;
		else
			if (blank_time) back_porch <= 1'b0;
	end
	

endmodule 


nmi207

Code: Select all

/*

ZX81 ULA implementation

Andy Rea march/april 2011

*/

// this module runs off the 3.25Mhz clock
// has a reset (and hold) input
// and 2 outputs
//
// the reset signal when asserted will set the count to 1 
// and hold it there for as long as the reset signal is asserted.
//
// bit4 is just bit 4 of the binary count and is used to time
// the back_porch generation in the video.
//
// last 16, is bits 6 & 7 logically anded
// and gives a 16 clock cycle pulse that is used
// to generate Hsync's and the nmi pulses
//
// when left free running (reset de-asserted)
// the count increments by 1 on each cycle


module nmi207 (

clock3,
reset,

bit4,
last16
);

input clock3;
input reset;

output bit4;
output last16;

reg [7:0] count;


assign bit4 = count[4];
assign last16 = count[6] & count[7];



always @ (posedge clock3)
	begin
	
	if (!reset & (count != 8'd207)) 
	begin
	count <= count + 1'd1 ;
	
	end
	else
	
	count <= 8'd1 ;
	
	end
endmodule
ferd

Code: Select all

/*

ZX81 ULA implementation

Andy Rea march/april 2011

*/

// this module handle the keyboard io along with tape io and the uk/us bit

//outputs on the databus are never driven high, only low
// this is done because every way i tried i couldn't get the
// output to go open drain if they drove high at any time.



module ferd (

ferd,
force_nop,
kbd,
uk_us,
tape_in,

data_out
);

input ferd;
input force_nop;
input [4:0] kbd;
input uk_us;
input tape_in;

output [7:0] data_out;

// what a load of tosh....
// bit convoluted but it was the only way i could be sure the databus pins
// will be open drain.
// by never driving a logic '1' but 'z' instead
// 
// if any drove a '1' the pin was no longer open drain :(

// each line if force_nop is true, drives '0' else it then evaluates ferd
// if ferd is true it then evaluates kbd input if that is true drives 'z' else drives '0'
// if ferd was not true frives 'z'



assign data_out [0] = (force_nop ? 1'b0 : (ferd ? (kbd [0] ? 1'bz : 1'b0 ) : 1'bz ) );
assign data_out [1] = (force_nop ? 1'b0 : (ferd ? (kbd [1] ? 1'bz : 1'b0 ) : 1'bz ) );
assign data_out [2] = (force_nop ? 1'b0 : (ferd ? (kbd [2] ? 1'bz : 1'b0 ) : 1'bz ) );
assign data_out [3] = (force_nop ? 1'b0 : (ferd ? (kbd [3] ? 1'bz : 1'b0 ) : 1'bz ) );
assign data_out [4] = (force_nop ? 1'b0 : (ferd ? (kbd [4] ? 1'bz : 1'b0 ) : 1'bz ) );

//assign data_out [4:0] = ( force_nop ? 5'b00000 : ( ferd ? kbd [4:0] : 5'bzzzzz ) ) ;
assign data_out [5] = (force_nop ? 1'b0 : 1'bz );
assign data_out [6] = (force_nop ? 1'b0 : (ferd ? (uk_us ? 1'bz : 1'b0 ) : 1'bz ) ) ;
assign data_out [7] = (force_nop ? 1'b0 : (ferd ? (tape_in ? 1'bz : 1'b0 ) : 1'bz ) ) ;


	
endmodule
state

Code: Select all

/*

ZX81 ULA implementation

Andy Rea march/april 2011

*/

//small state machine to control the sequence of events during a video cycle

module state (
clock6,
m1,
halt,
a15,
d6,

force_nop,
alt_address,
load_vshf
);

input clock6;
input m1;
input halt;
input a15;
input d6;

output force_nop;
output alt_address;
output load_vshf;

wire vid_detect;
wire vid_set;
wire vid_reset;
wire alt_address_start;
wire alt_address_stop;
wire force_nop_start;
wire force_nop_stop;
wire load_vshf_start;
wire load_vshf_stop;
reg vid_cycle;
reg force_nop;
reg alt_address;
reg load_vshf;

reg [2:0] q ;

assign vid_detect = !d6 & a15 & halt & !m1 ;
assign vid_set = (vid_detect & (q == 3)) ;
assign vid_reset = (q != 0) ;

always @ (posedge vid_set or posedge vid_reset) begin
	if (vid_set) vid_cycle = 1'b1 ;
	else vid_cycle = 1'b0 ;
end
 

assign force_nop_start = (vid_cycle & !clock6 & (q == 3)) ;
assign force_nop_stop = (!clock6 & (q == 4)) ;

always @ (posedge force_nop_start or posedge force_nop_stop)
	begin
		if (force_nop_start) force_nop <= 1'b1 ;
		else force_nop <= 1'b0 ;
	end


assign alt_address_start = ((q == 5) & vid_cycle ) ;
assign alt_address_stop = (!clock6 & (q == 7)) ;

always @ (posedge alt_address_start or posedge alt_address_stop)
	begin
		if (alt_address_start) alt_address <= 1'b1 ;
		else alt_address <= 1'b0 ;
	end


assign load_vshf_start = (!clock6 & (q ==  6) & vid_cycle) ;
assign load_vshf_stop = (!clock6 & (q == 7) ) ;

always @ (posedge load_vshf_start or posedge load_vshf_stop)
	begin
		if (load_vshf_start) load_vshf <= 1'b1 ;
		else load_vshf <= 1'b0 ;
	end



always @ (posedge clock6)
	begin

	if (q == 0)
		if (!m1) q <= 3'b001 ;
	else
		q <= 3'b000 ;
		
	if (q == 1) q<= 3'b010 ;

	if (q == 2) q<= 3'b011 ;

	if (q == 3) q<= 3'b100 ;

	if (q == 4) q<= 3'b101 ;

	if (q == 5) q<= 3'b110 ;

	if (q == 6) q<= 3'b111 ;
	
	if (q == 7) q <= 3'b000 ;
		
		
	end
	
	
endmodule
vshf

Code: Select all

/*

ZX81 ULA implementation

Andy Rea march/april 2011

*/

// this is the video shifter, it takes the data on the bus and loads 
// it in parralell and then shifts it out serially at 6.5Mhz
// when not loading new data zero are shifted in at the lsb end
// the output is the msb end, and each shift is from lsb to msb
//
// there is also an invert signal that is took into consideration
// during a load the data bus can be inverted to produce inverted video
// this is a per load signal and is derived from bit 7 of the char-code
//
// additionally, the back porch is mixed in here, so when the blanking
// is asserted during a load cycle or a shift cycle the output bit will
// remain a logic 1
//
// the video stream is inverted from the shifter to the final output
//
// in zx81 video data is stored in the rom inverted


module vshf (

clock,
data_in,
invert,
blanking,
load_vshf,

vid_out
);

input clock;
input [7:0] data_in;
input invert;
input blanking;
input load_vshf;

output vid_out;

reg [7:0] shifter;

wire vid_out = !shifter [7];  // inverted.



always @ (posedge clock)
	begin
	
	 if (load_vshf) begin
		
		shifter [7] <= ( blanking ? 1'b1 : (data_in [7] ^ invert) );
		
		shifter [6:0] <= (invert ? ~data_in [6:0] : data_in [6:0] );
	end else begin
		shifter [7] <= (blanking ? 1'b1 : shifter [6]);
		shifter [6:1] <= shifter [5:0];
		shifter [0] <= 1'b0 ;
	end
end
 
	
endmodule
what's that Smell.... smells like fresh flux and solder fumes...
sirmorris
Posts: 2811
Joined: Thu May 08, 2008 5:45 pm

Re: ULA revistited.

Post by sirmorris »

<more loud applause>
Post Reply