Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

vhdl project 1

Status
Not open for further replies.

mickvick

Programmer
Apr 19, 2004
3
GB
Please help,
I am i completely stuck on my project. I know what i want to do, but I am not sure where to begin by coding it.
I am implementing a scoreboard using VHDL. I am using a XS40 board to run my code on. The code will count down like a sports scoreboard, and will show the clock counting down on the monitor using the on board VGA.
I have written code which should count down from 12 minutes (12:00:00) to zero seconds, and now I have to figure out how I intend to show this on the screen.
I have broken the monitor into blocks of pixels, 32 blocks across and 24 blocks down. This makes each block 10 pixels each horizontally and 20 vertically.
i have then designed what each pixel would look like on the screen, using a box which is 4 blocks across (40 pixels) by 8 blocks down (160 pixels)
So, in order to set up my design, I intend to have a counter which will count every 10 pixels horizontally for each block, then count every 4 blocks of 10 pixels for each block, then count across in digits. i similar operation would take place vertically.(values of 20, 8, and 3)
I am sorry about not being able to explain this well, but i can post diagrams which will help explain this.
I then intend to store in ROM, the addresses of each block, and whether it will be white or black, and then project it on to the screen. I know the value I have in each address, but I am unsure as to how to make sure they appear how I want them to using the VGA.
I have no idea as to how I am going to implement my design in code. I am not an experienced programmer in VHDL and I only have another 2 weeks to finsh this project.
Here is the initial code I have written for the counters, but have yet to simlute and synthesise.

Library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity GAMECLOCK is
port (
CLOCK: in STD_LOGIC;
RESET: in STD_LOGIC;
RUN : in STD_LOGIC;
DEC : in STD_LOGIC;
umin: out STD_LOGIC_VECTOR (3 downto 0);
lmin: out STD_LOGIC_VECTOR (3downto0);
usec: out STD_LOGIC_VECTOR (3 downto 0);
lsec: out STD_LOGIC_VECTOR (3 downto0);
umsec: out STD_LOGIC_VECTOR (3 downto 0);
lmsec: out STD_LOGIC_VECTOR (3 downto 0));

end GAMECLOCK;

architecture of GAMECLOCK is

SIGNAL dec_umin, dec_lmin, dec_usec, dec_lsec, dec_umsec, dec_lmsec : STD_LOGIC_VECTOR (3 downto 0);

begin

lmsec:process (CLOCK, RESET)
begin
if RESET = '1' then
lmsec <= "0000";
dec_umsec <= '0';
elseif CLOCK'event and CLOCK = '1' then
if lmsec = "0000" then
lmsec <= "1001";
dec_umsec <= '1';
else
lmsec <= lmsec - '1';
dec_umsec <= '0';
end if;
end if;
end process
umsec:process (CLOCK, RESET)
begin
if RESET = '1' then
umsec <= "0000";
dec_lsec <= '0';
elseif CLOCK'event and CLOCK = '1' then
if umsec = "0000" then
umsec <= "1001";
dec_lsec <= '1';
else
umsec <= umsec - '1';
dec_lsec <= '0';
end if;
end if;
end process
lsec:process (CLOCK, RESET)
begin
if RESET = '1' then
lsec <= "0000";
dec_usec <= '0';
elseif CLOCK'event and CLOCK = '1' then
if lsec = "0000" then
lsec <= "1001";
dec_usec <= '1';
else
lsec <= lsec - '1';
dec_usec <= '0';
end if;
end if;
end process
usec:process (CLOCK, RESET)
begin
if RESET = '1' then
usec <= "0000";
dec_lmin <= '0';
elseif CLOCK'event and CLOCK = '1' then
if usec = "0000" then
usec <= "0101";
dec_lmin <= '1';
else
usec <= usec - '1';
dec_lmin <= '0';
end if;
end if;
end process
lmin:process (CLOCK, RESET)
begin
if RESET = '1' then
lmin <= "0010";
dec_umin <= '0';
elseif CLOCK'event and CLOCK = '1' then
if lmin = "0000" then
lmin <= "0010";
dec_umin <= '1';
else
lmin <= lmin - '1';
dec_umin <= '0';
end if;
end if;
end process
umin:process (CLOCK, RESET)
begin
if RESET = '1' then
umin <= "0001";

elseif CLOCK'event and CLOCK = '1' then
if umin = "0000" then
umin <= "0001";
else
umin <= umin - '1';
end if;
end if;
end process
end GAMECLOCK;
If any one could help or requires more information, please let me know,
Cheers,
T
 
Firstly VHDL is very strongly typed, so some things I will point out from your code (maybe just typo's).

dec_umsec is declared as std_logic_vector of 3:0 but you assign it as follows:
dec_umsec <= '0';
Either the declaration should be for type std_logic or your assignment should be 4 bits, or you should indicate which bit you wish to be '0'.

Ie dec_umsec <= "0000"; -- or
dec_umsec(0) <= '0';

Looks to me like you didn't mean to declare all those signals as std_logic_vector(3 downto 0). Try std_logic.
--------------------------------------------------------
So looking at the code. dec_umsec is asserted every 9 clocks. I assume you have figured that out based on your clock speed and how often you want that signal asserted

So far so good.

You then have a umsec signal which again drops from 9 to 0. and then assertes a signal called dec_lsec. However it also seems to decrement every clock. I am guessing that you missed an if statement after the clock'event. That if statement i imagine should have been
if dec_umsec = '1' then
... -- all your current code.
end if;

That way umsec only decrements when dec_umsec occurs.

Note however that this will change your dec_lsec signal from being a 1 clock asserted signal to being a signal that is asserted untli the next occurance of dec_umsec.

So I would modify what I wrote above too:
if dec_umsec = '1' then
... -- all you current code.
else
dec_lsec <= '0';
end if;

The same logic goes for all of your counters.

I am not sure on the display part of your project. I would need more info. Maybe this will get you started and you can post any further questions as you get to them.

--
 
I forgot to address your blocks/box/rom issue.

So you have 32 x 24 blocks which equates to 8 x 3 boxes (each 4 x 8 blocks).

Hopefully you have some control over how you use your ROM and your screen. I guess you have to write to the display in a sweeping manner horizontally, dropping a line each time. In which case you want to try and organise you ROM in a similar manner.

I assume each character fits in one box. and that all the pixels in one block will be the same (so you character will be made up of 4 x 8 chunks).

Actually thinking about this... I am not sure what you intend to do with your ROM. Will you write the data into the ROM as it is seen on the screen (so one bit per pixel - or one bit per block). Or will you simply write each valid character into the ROM so that you can look up the information based on your counter values?

If you wanted it too look more like a computer situation (where software would be writing to the ROM) its probably better to write one bit = one pixel or block. But for dedicated hardware the other method would take less space (but may be harder to expand your features).

I will assume one pixel/block = one rom bit method rather than storing the character definition in ROM (which is kind of what we will do anyway).

First off you will want to figure out how to draw your characters 0-9 and : (and whatever else you want).

So, the one pixel/block = one rom bit method would require you to create constants for all your characters. If you were to do it per pixel it would be huge, so I am guessing you are going for the 1 bit per block.
Either way you might want to look up packages and put the constant declarations in a package.(maybe to tidy up at the end of your project). each character will be 4 x 8, so create an array type (block_t) of std_logic_vector(3 downto 0) with a range of (7 downto 0), that will give you a 4 x 8 array. Then create another type which is an array of your first type. Then declare a constant of this second type. You now have a constant that you can address with your counters. You can further decide that ':' is say array entry 10 and add that to you table if you require. or you can just create a seperate constant for that.

type block_t is array (7 downto 0) of std_logic_vector(3 downto 0);
type numeric_char_t is array (9 downto 0) of block_t;

constant chars : char_type = (("0000",X"F", ... -- all 8 entries, 4 bit each), -- entry for char 0.
("0000","0001",... -- all 8), -- entry for char 1.
etc.

now you can say chars(0) to get an array of 8 x 4 for numeric 0.
or chars(0)(0) to get the first (top or bootom, you choose) line of 4 blocks for numeric 0.

Which also means you can use chars(conv_integer(lsec))(0) to get the first line of blocks for the character that is set the counter lsec.

I'll leave you there and you can ask any further questions. I'll just add that you have to think about how to address your rom... You will need to write and read from it, and whatever you do, the algorithm will be slightly different for both since you will probably write to it per character, and read from it per horizontal line. Or you can create your horizontal line before writing and write that way too, up to you and the structure of your rom i guess. (again this is assuming that you need to write to the screen in horizontal sweeps)




--
 
Sorry for troubling you again vhdl guy. I am very much appreciative of your help. I am still experiencing problems involving this vga issue.Thanks for your advide before.

I have resolved most of the problems with the decrement counter, however, during simulations i realised that when the counter changed from a whole number of minutes, such as 12:00:00, the counter only decremented in the smallest value, rather than changing to 11:59:59. I am in the process of changing this error.

In terms of my memory issue and the VGA, I have decided to assign each bit of memory to each block ( 20 pixels by 20 pixels, and have each digit as 4 blocks wide by 8 blocks tall. My problem is calling these addresses, so they know where each is on the screen.

At the minute, my screen looks like this

12:00:00

(where each digit (i.e '1' will take up 80 pixels)

So, I decided to use the dual port ram on the chip and store what each image would look like in memory.

I want to have a counter at the top left hand corner of the screen that counts across and down. I can therefore deduce that for instance a 1 in the example above will take up the pixels spaces 1320-1339
1960-1979
2600-2619
3240-3259
3880-3899
I worked this out, by constructing an image for each digit, placing it on the screen and then working out which pixels it would take up. In the case of 1, the block below will always be 640 pixels more...and so on.

I will still have a counter which counts the number of pixels (20) to increment 1 block, which increments each digit (4 blocks) which increments at the end of the screen.

I am using the XS40 board to do this.

Once again, sorry about the trouble, but I have put hours into solving this, and cant manage to formulate any code. Does anybody know of any source code available that would do such a job of readig from the Countdown timer, taking an image from the RAM and then displaying it on the screen?

Thanks in advance.
 
Ouch. So you want to store EVERY pixel in the rom? If you only want to store information per block it would be much easier (since I think you will be lighting every pixel per block if required - much easier to store that kind of information).

Anyway, assuming 1 pixel = 1 rom bit.

So your counter - vga mapping is:

count 0 = top left corner (1st line)
count 639 = top right corner (1st line)
count 640 = second line, top left
count 1279 = second line, top right.

Is that correct?

Given that you want to do it this way we will have to work out a way to calulate the pixels you require, otherwise its too much information.

For example. We could still create array's for each digit, and create a LUT so that we can decide per block if the block is to be light or not. As per my last email you can then have a 10 digit lookup with 8x4 bits per digit.

We can then create some kind of calulation based on that lookup and a position number (where you want it to go on the screen).

Actually doing the calculation might be too much, 640x480 gives you a 19 bit number, and doing multiplication etc can be tough to meet timing and also chews up your resources. So lets keep track of everything in counters.

Create a counter to counter to 20 called horizP (horizontal Pixel).
another to count to 4 called horizB (Block)
another to count to 8 called horizD (Digit).

A counter to count to 20 called vertP (Vertical Pixel)
A counter to count to 8 called vertB (Block)

create a nested if statement based on these counters

if horizP = 20 then
horizP <= 0;
if horizB = 4 then
horizB <= 0;
if horizD = 8 then
horizD <= 0;
if vertP = 20 then
vertP <= 0;
if vertB ....
-- you can continue from here.


else
vertP <= vertP + 1;
end if;
else
horizD <= horizD + 1;
end if;
else
horizB <= horizB + 1;
end if;


You get the idea.

Now all you have to do is create another counter with your ram address and have it count from 0 to 640x480. Make sure that all your counters reset together and move together. So now you have a rom address, and a bunch of counters that you can use to determine the correct pixel value.

for example, based on my last posting:
pixel = chars(thenumber)(vertB)(horizB);

To make everything fit better, you probably should change my chars type from my last post so that all the ranges are to rather than downto:
chars(0 to 9)(0 to 7)(0 to 3) that way it matches the top left = count 0 approach that we used in the rom. (just makes it less confusing when you try to create your chars LUT.

Note that "thenumber" is the digit that will appear on the screen. So it will come from your 12 minute count down timers. So you will probably end up with an array to replace that based on horizD. put all of your counters in an array so that you can address them that way. You can leave them the way you have already written them, and assign the old counter values to the new array locations outside of a process.

ie
digit(0) <= umin;
digit(1) <= lmin;
digit(2) <= 10; (10 could be : in your chars array)
digit(3) <= usec;

etc

If that is not in a process then it is just a wire - you could look at it as a name change for your signal, in this case you are changing the name to an array to help with your counters.

Let me know if any of this doesn't make sense.

--
 
vhdl guy,
I think that i have implemented the counters both horizontally and vertically, and they increment correctly. I have also decided on the addresses in the ROM, but I am under the impression for the datasheet that I can download these addresses onto the RAM as part of a UCF file. #

My main predicament is the formalising the LUT's. You discussed in your previous post about storing the RAM addresses as an LUT, and then getting the program to call them once I have my desired value. i assume that this is when my counters produce a zero for instance, and the program looks up what image such a value would produce.

Would it be possible for you to show me how these LUT's would be called in the main program.

Once again, i am very sorry for the hassle.

Thanks in advance.
 
The idea with my counters is to have a LUT for your blocks.
So each character would take a LUT of 8x4.

This would simply be implemented as an array of constants.
lets say that 0 is

1111
1001
1001
1001
1001
1001
1001
1111

then you would create the following:

type block_t is array (7 downto 0) of std_logic_vector(3 downto 0);
type numeric_char_t is array (10 downto 0) of block_t;

constant chars : numeric_char_t :=
(("1111","1001", "1001", "1001",
"1001", "1001", "1001","1111"),
(same for char 1),
(same for char 2),
(...),
(same for char 9),
(same for char :));

you can then address chars 0-9 and make : as 10.

So as per my last posting:
pixel <= chars(thenumber)(vertB)(horizB);
where thenumber is your counter value 0-9 or 10 for :

So basically for each count (horiz and vert counts) you are figuring out your pixel location, then you use the LUT to decide if that particulat location should be on or off.

I am not sure what you mean about your ROM addresses. But lets say you have a rom with 307200 bits total (640x480).
Maybe it is arranged as 9600 lines x 32 bits.

So if you decide that address 0, bit 0 is the top left corner of your screen. then address 0 bit 31 is the 32nd bit on the top line of your screen. the 33rd bit on the top line is address 1, bit 0.

So in that case you could have an address counter and a bit counter. And every time you increment your vert/horizontal counters you increment your ram address counters.

You might then add a bit of "buffering" into writing to your ram. each time you increment the bit address of your ram, just write to a seperate 32 bit register (one bit at a time, addressed by your bit counter). Then when you get to bit 31 you can write the whole line into ram at once.

Of course you may only have a 16 bit wide ram, or maybe a 8 bit wide. thats all just implementation details.

The real issue is having two sets of counters, one to keep track of your location that allows you to easily address the ram, and a second to keep track of your location that allows you to easily address your LUT. Make sure that these counters start from the same point and always increment at the same time, and you now have your working system.

So now you have:
buffer(ram_bit) <= chars(thenumber)(vertB)(horizB);

and some code that basically says
when ram_bit gets to 31 then
ram(ram_addr) <= buffer;

Note that you may need to wait until buffer gets the 31st bit written to it. One way to do that for example is to keep a ram_addr and a last_ram_addr which increment at the same time, but last_ram_addr is 1 less than ram_addr. Then the above would be:
if ram_bit = 0 then
ram(last_ram_addr) <= buffer;


of course, you will not write ram(last_ram_addr). It would probably be something more like:

-- combinatorial logic
time_to_write <= '1' when ram_bit = '0' else '0';

(this is assuming that ram_bit only stays 0 for 1 clock - otherwise you would need some other logic).


-- your ram process
if time_to_write = '1' then
Data <= buffer;
Address <= ram_addr;
write_l <= '0'; -- write_l - 0 indicates write
elsif time_to_read = '1' then
-- whatever you are doing to get data from ram to screen.
end if;



One more note. Make sure that you types match between counters and LUT. For example if you use an integer type for your counters, make sure that they have a range that is the same as the array range.

And if you use std_logic_vector or unsigned as your counters, then you will need to type cast your counter to an integer to use it as an address to the array.

You can convert using conv_integer(vector_name) if you declare the ieee std_logic_unsigned package (which it looks like you already did). That will work for both std_logic_vector and unsigned.

--
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top