Author Topic: Verilog: Adding "slices" of numbers  (Read 4014 times)

0 Members and 1 Guest are viewing this topic.

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Verilog: Adding "slices" of numbers
« on: February 09, 2022, 06:44:21 pm »
Hi all.

Looking for some advice for adding two numbers, where I want number A to be added to number B starting from bit position 4 to make number C, so that would look like:

AAAAAAA
    BBBBBBB
CCCCCCCCCCC

The specific reason for wanting to do this is to form a memory address, where number A represents an offset into memory, and number B represents an index from that offset.

Number A in this case will increment the offset in blocks of 80 (decimal), and number B will increment in single steps from that location, hence its not a simple concatenation.

What would be the best way to achieve this in Verilog?

Im trying to use the least number of macrocells in my CPLD as possible, as I only have 160 of them to play with in total. The application is a CRTC for a video card for a retro computer system I am building. The end goal is to generate 25 rows of 80 column text, with each character being 9x16 pixels. Im currently targeting an Altera EPM7160.

Thanks!
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #1 on: February 09, 2022, 08:51:27 pm »
Never mind, it seems it is basically as simple as it would be in something like C (language).

C = (A << 4) + B;

Problem solved, ModelSim shows that it is working perfectly.  8)
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11721
  • Country: us
    • Personal site
Re: Verilog: Adding "slices" of numbers
« Reply #2 on: February 09, 2022, 11:28:09 pm »
Another typical way of doing this something like this:
Code: [Select]
wire [10:0] c = { a, 4'b0000 } + { 4'b0000, b };

This code is explicit about size of the values and easier to read, IMO. This approach is also better if you need to have carry in/out values too.
Alex
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8084
  • Country: ca
Re: Verilog: Adding "slices" of numbers
« Reply #3 on: February 09, 2022, 11:34:48 pm »
Another typical way of doing this something like this:
Code: [Select]
wire [10:0] c = { a, 4'b0000 } + { 4'b0000, b };

This code is explicit about size of the values and easier to read, IMO. This approach is also better if you need to have carry in/out values too.
Without the size of 'c' being implicit, quartus may sometimes strip out the MBS bits especially if 'c' is used in an IF statement or you want to analyze the carry bit, IE: 'wire [11:0] c = { a, 4'b0000 } + { 4'b0000, b };'

Writing the 'wire [11:0] c' does prevent this simplifications.  As for the braces {}, they make it easier to visualize the '0's or you may force those '0' to any other valuer you like.
« Last Edit: February 10, 2022, 03:54:52 am by BrianHG »
 

Offline SMB784

  • Frequent Contributor
  • **
  • Posts: 421
  • Country: us
    • Tequity Surplus
Re: Verilog: Adding "slices" of numbers
« Reply #4 on: February 10, 2022, 03:08:52 pm »
Throw my advice on the pile with everyone elses: expand word length to WL+1 and pad summands with zeroes to achieve desired  summation.

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #5 on: February 11, 2022, 12:28:51 pm »
I dont need any carry, I should never actually overflow anyway. I only need values in the range 0-1999 for 25 rows of 80 column text. :-)

But, its working - flawlessly even, on the first attempt! Theres a lot to be said about simulation and test benches!  8)

The design came in under 100 macrocells (so far, little bit more to add for an external hardware cursor). I was struggling to fit the same basic design into a 128 macrocell CPLD using CUPL. Quartus' compiler has certainly worked some magic on this.
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #6 on: February 11, 2022, 12:37:53 pm »
Well, I say flawlessly, and now Ive just noticed there are some repeating blocks of characters. So maybe theres a little more work to do...

e.g. the second line of text is repeating what is on the first line one or two characters after the end of the message.

Starting to worry now.  :-[
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8084
  • Country: ca
Re: Verilog: Adding "slices" of numbers
« Reply #7 on: February 11, 2022, 01:37:03 pm »
Have you chosen then EPM7160 because you have them lying around?

The smallest max10 10M02 series is much cheaper and is available in 144pin QFP, single supply 3.3v, built in boot-prom, with enough logic elements and built in ram to run your ASCII display, multiple pages, entirely internally as well as being capable of outputting up to 720p HDMI with audio directly without any HDMI transmitter ICs.  It also has 2 PLLs.

Not to mention enough spare room for a tiny MCU or sound engine if you like.  Note that the HDMI audio eats a lot of logic cells where using DVI out mode eats near nothing.
« Last Edit: February 11, 2022, 01:39:26 pm by BrianHG »
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #8 on: February 11, 2022, 04:01:33 pm »
More or less, yes. Im working with the EPM7000S series (and Atmel ATF150x's too) because they are 5V, and Im using them in retro style projects. So I'd like to use them. :-)

I'd really rather avoid 3.3V devices in 5V projects because I'd really like to avoid any level shifting/special handling required to interface with 5V stuff.
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #9 on: February 11, 2022, 04:27:24 pm »
And there is indeed some kind of problem with the address generation.

My current syntax is as ataradov suggested

Code: [Select]
assign ga_bus = {text_row_val, 4'b0000} + {4'b0000, text_col_val};

If someone has the time to take a look I'd appreciate some help on this one. This adding of two counters together has been giving me grief across two HDLs. :)

https://github.com/tomstorey/vga_controller/blob/master/VGA_1.v#L117

(Slightly older version of code, Im currently using the line of code quoted above in place of L117 in the git repo).
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #10 on: February 11, 2022, 05:17:54 pm »
This image may help if you are looking through the code.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8084
  • Country: ca
Re: Verilog: Adding "slices" of numbers
« Reply #11 on: February 11, 2022, 07:24:58 pm »
More or less, yes. Im working with the EPM7000S series (and Atmel ATF150x's too) because they are 5V, and Im using them in retro style projects. So I'd like to use them. :-)

I'd really rather avoid 3.3V devices in 5V projects because I'd really like to avoid any level shifting/special handling required to interface with 5V stuff.

This image may help if you are looking through the code.


    All of that would fit inside the 10M04SCE144, (note that the 10M02 is actually a 10M04 if you tell Quartus just to compile and program for a 10M04).  The 10M04 has more than 16kb ram plus 4k logic cells.   Enough to create a full terminal software emulator on FPGA.  IE: add accelerated clear-screen, CR/LF page scroll, more than 1 font or 512 character support.  Using the FPGA internal blockram also allows you to have a power-up memory contents like powering-up with a preset font and preset onscreen message text.

    Since all the external interface controls on the MAX10 would be exclusively an input, a simple series resistors from your MCU to the PLD/FPGA can be used to drop the 5v TTL to a 3.3v.  Only the outputs to your monitor would be lower voltage, in this case 6 or 12 outputs through resistors as your VGA dac, or 8 outputs to a 99cent HDMI cable driver amp. (Source&Schem Here)  The max10 for this app will draw maximum around 150ma with HDMI output, 50ma using a resistor DAC.

     However, thanks to the damn chip shortage means not getting your hands on any of these for quite awhile.  These FPGAs supposed to be ~10$ each.

 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8084
  • Country: ca
Re: Verilog: Adding "slices" of numbers
« Reply #12 on: February 12, 2022, 12:46:09 am »
Im trying to use the least number of macrocells in my CPLD as possible, as I only have 160 of them to play with in total. The application is a CRTC for a video card for a retro computer system I am building. The end goal is to generate 25 rows of 80 column text, with each character being 9x16 pixels. Im currently targeting an Altera EPM7160.

Thanks!

Just so I understand, you would not accept 25 rows by 80 columns display, but 32 row by 128 column in ram though you may have enough ram?  I am not sure about your code, but, this project properly written should squeeze into ~50 macrocells.  This would include the posibility of scrolling the display screen smoothly off the borders, but that would eat another ~25 marcocells.

You are using 9x16 instead of 8x16, is this correct as the pixel clock and generating that extra pixel does eat some logic?

If all you need is 64 colors, 2bit RGB, you might be able to use 6 digital outs with a 2 resistor dac on each output.

As for finding your read data 'address' on the new line, all you need is 2 counters.
Counter #1, 11bit, resets at V-sync & increments by 80 (or a user set amount) once every 16 active H-Sync lines.
Counter #2, 14bit, every H-sync loads counter #1 multiplied by 8 (IE: {counter#1,3'b000}) , and increments for each pixel while the bottom 3 bits are used for the character rom's 8 bits, indexing which bit of the character to display.  (You may also use 11bits here and the main H counter's bottom 3 bits for the font index, but no more horizontal smooth scrolling feature if you wanted to add such a feature.  Remember, only the upper 11bits are sent to the display ram address.)


The is the most economical means of computationally generating your read address and FONT table bit pointer index.

Now when I say <50 macrocells, I mean you only have an system wide 10bit H and 9bit V counter + counter #1(11bit) + read address counter#2(14bit).
That's 44 macrocells total, plus 2 for the HV sync output plus another 4 for your P_BUS output.

I would still make the counter #1 16 bits and counter #2 19 bits and make a 64k display ram for over screen size scrolling video games.  If you code the system as compact as I say, with the programmable H-increment for counter #1 + a V-sync starting address, you can make over-sized screen scrolling games up to 64kb of text, including 4 sprites and place the font and color ram all in 1 8bit ram chip & optional rom.  Well done, there should be just enough space in a EPM7160 to do all this with some smart coding including some true 1 bit color graphics modes.

Note that for your font rom, I would have made it compatible with the standard PC/Atari/Commodore font layout where the lower 4 address bits contain the Y coordinate/line in the font and the upper address bits contain the character index.  However, I have not read deeply into your code.  This makes it faster to change character's font or generate animation like the old video games.
« Last Edit: February 12, 2022, 03:31:36 am by BrianHG »
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #13 on: February 12, 2022, 10:12:11 am »
Just so I understand, you would not accept 25 rows by 80 columns display, but 32 row by 128 column in ram though you may have enough ram?  I am not sure about your code, but, this project properly written should squeeze into ~50 macrocells.

That was my original plan, although at the moment Im trying to achieve the use of some 2K DPRAMs - the video card side is constantly set to output, and the CPU can then write into the other side without needing to go through all of the bus time sharing malarky. My original design had two banks of RAM so that the video card would be reading out of one, and the CPU could read/write the other, and then set a flag to switch the banks at the next vsync. Havent ruled this out yet, but if I can make it work with the 2K DPRAMs I'll be very happy for now.

If I can make the current method work, then I can e.g. take some 8K standard RAMs, have two banks, and then have 4 pages in each bank, as an example. So there are still some advantages to be had from making this work I think.

Quote
You are using 9x16 instead of 8x16, is this correct as the pixel clock and generating that extra pixel does eat some logic?

Yep thats correct. Most characters are only 8 pixels wide, with the 9th pixel being a spacer (results in a H res of 720 as opposed to 640). But box drawing characters in the 0xCX and 0xDX range get stretched out to 9 pixels so they meet up with their right hand neighboring column for continuous lines.

Colour wise Im doing 16 foregroung colours and 8 background colours, with the MSb indicating blinking text - basically classic VGA text mode (that I grew up with anyway).

Quote
Note that for your font rom, I would have made it compatible with the standard PC/Atari/Commodore font layout where the lower 4 address bits contain the Y coordinate/line in the font and the upper address bits contain the character index.  However, I have not read deeply into your code.  This makes it faster to change character's font or generate animation like the old video games.

I hadnt actually looked how a standard font ROM was organised, so I just whipped something up for proof of concept purposes. For sure I can change it easily enough once I get everything working, if it would make it easier for people to copy existing fonts. Eventually the font ROM will be banked to allow switching between multiple character sets, but that will happen externally to the CPLD, so none of that is included.

Hardware scrolling isnt something I had intended to support for this first version.

Lots of good ideas for a future version though, and Im tempted to build something more capable. But for now I really just want to fix this adder issue and I'll have reached my current goal.  ^-^

I'll play around with some different counter bit widths and see if something else will work. I have an Altera programmer on the way, wont be here until next week, so for the time being if I can fit it in 128MCs then I can still program my existing 128MC Atmel CPLD, and then switch to 160 if I run out of room.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8084
  • Country: ca
Re: Verilog: Adding "slices" of numbers
« Reply #14 on: February 12, 2022, 11:33:50 am »
Lots of good ideas for a future version though, and Im tempted to build something more capable. But for now I really just want to fix this adder issue and I'll have reached my current goal.  ^-^
Give my text in blue a shot.  It should work because it is not an adder, but a multiplier.
IE: once every 16 lines, you want to resolve 0x80, 1x80, 2x80, 3*80,,, for the beginning after the H-Sync, then standard increment.

Unless you are doing a lot more than what's shown in your block diagram, you should be able to shrink and consolidate your design way down to around 50 logic cells.  Especially if you use 1 H and 1 V counter to drive everything from a sync generator and your ram addressing as well, you should be able to shrink your existing design down to ~30 logic cells leaving a ton of room in your 128 cell pld.
« Last Edit: February 12, 2022, 11:51:56 am by BrianHG »
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #15 on: February 12, 2022, 10:01:40 pm »
I took the idea of using an 11 bit counter that keeps track of the "row" by incrementing by 80 at a time, and the value of that counter is loaded into a second counter each scanline and then incremented by 1 to form the address that is output to the RAMs. I believe the issue is now resolved. :-)

Tested by writing to every line on the screen individually with a known pattern, and no unwanted repeats now.

Using 94 MCs now, too. :-)


Counter #2, 14bit, every H-sync loads counter #1 multiplied by 8 (IE: {counter#1,3'b000}) , and increments for each pixel while the bottom 3 bits are used for the character rom's 8 bits, indexing which bit of the character to display.

Im a little unsure about this part because although the characters are 8 bits wide in the ROM, they are formatted as 9 bits wide on the screen. Presumably I would have to do some clock stretching to make this work.

Perhaps a lot of the additional logic is taken up by my 9 bit shift register method in pix_gen.

But now that I have a working solution, I can play around with different methods to achieve the same result. :-)

Thanks for your help!
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8084
  • Country: ca
Re: Verilog: Adding "slices" of numbers
« Reply #16 on: February 13, 2022, 12:51:54 am »
Counter #2, 14bit, every H-sync loads counter #1 multiplied by 8 (IE: {counter#1,3'b000}) , and increments for each pixel while the bottom 3 bits are used for the character rom's 8 bits, indexing which bit of the character to display.

Im a little unsure about this part because although the characters are 8 bits wide in the ROM, they are formatted as 9 bits wide on the screen. Presumably I would have to do some clock stretching to make this work.

Perhaps a lot of the additional logic is taken up by my 9 bit shift register method in pix_gen.

But now that I have a working solution, I can play around with different methods to achieve the same result. :-)

Thanks for your help!

Ahhh, ok.  This is a strategic coding issue as I would be targeting the minimum LCs, my approach would have been having 1 extra LCs which would activate every time the hcounter[2:0] are =3b'111.  That logic cell, I would name 'stall' would be an enable for the horizontal counter.  When set, it will not allow the hcounter to inc for 1 clock, when set it will also clear on the next clock.

The 'stall' would allow for the repeat pixel used in the IBM 9x16 font as well as have an input to ignore the stall to switch you into 8x16 mode for graphics and gaming support at the expense of the 60hz mode being switched into a 68hz mode, or you may have an adjustment on the total h-count length to get back to 60hz.


Working you way up, I would say that with 160LC, if you smartly code everything, you should be able to use a single 8bit 128kb static ram chip and have a shared dualport emulator.  I cant gaurantee it would be easy, but doable unless your CPU needs > 25MHz access to memory.
 

Offline TomS_Topic starter

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: Verilog: Adding "slices" of numbers
« Reply #17 on: February 13, 2022, 04:57:06 pm »
Working you way up, I would say that with 160LC, if you smartly code everything, you should be able to use a single 8bit 128kb static ram chip and have a shared dualport emulator.  I cant gaurantee it would be easy, but doable unless your CPU needs > 25MHz access to memory.
Ah, but not in my PLCC84 packages, I dont have enough pins to do that. I'd definitely need to step up to something like a TQFP - but these CPLDs are kind of hard to come by, and expensive when you do.

Although it could probably be made to work with some external tri-state buffers, having only the control logic itself within the CPLD.

As it is though, I have 34 macrocells left to play with in my current CPLD (128MC). I'd imagine a simple bit of arbitration logic would fit within that quite easily, with leg room to spare.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf