This is only a preview of the May 2020 issue of Practical Electronics. You can view 0 of the 80 pages in the full issue. Articles in this series:
|
Want to give a project the retrocomputer look? Or do you just
need a convenient way to display
a screen full of text on a low-cost
monitor? Then this nifty project is
for you. It generates a VGA signal
akin to something from an early
PC, or even a Commodore 64 or
Amiga. It does this using a low-cost
iCEstick FPGA development board
and a very simple add-on board
and is controlled via a serial port.
iCEstick
Part 2 – creating an FPGA VGA Terminal
F
ollowing on from our review
of the iCEstick and IceStudio
software in last month’s issue,
we wanted to see if we could do something more useful and exciting than
flashing an LED.
After all, field programmable gate
arrays (FPGAs) are considerably more
capable than microcontrollers. So we
had to think of an application that
couldn’t be done with a 555 timer IC
or the most basic micro you can buy!
So we’ve come up with some
‘code’ which configures the FPGA
chip (an iCE40HX-1k) on the iCEstick
to generate VGA-compatible video
signals using the eight digital outputs
available on its PMOD connector.
The output is displayed as 16 rows
and 32 columns of text, with selectable
foreground and background colours
for each 8 × 8 pixel character.
The colours come from a
palette of 16, chosen from
64 possibilities. The graphics
ROM includes pseudo-graphics
characters to create block graphics, boxes and shaded regions.
The module is controlled by
a serial (UART) data line running
at 9600 baud which accepts regular
ASCII characters, LF, CR and FF
control codes, as well as the pseudographics above ASCII code point 127.
There are also control codes to set
the colours.
30
This project could be used as the
starting point of a more ambitious
FPGA-based project, or you could combine it with a microcontroller of your
choice, using the serial port as your
micro’s display interface to the VGA
monitor, to display text and graphics.
Note that when it comes to FPGAs,
it no longer makes sense to refer to
the code as ‘software’. The IceStudio
software takes our HDL (hardware
description language) and synthesises
it into a bitstream.
This bitstream could be considered
an equivalent to a binary firmware
image; it can be stored in a computer
file or uploaded to an EEPROM on
a target system. But rather than
being a sequence of
instructions
for
a processor
to execute, it describes
how the various elements within
the FPGA are connected or configured.
For more detail on this aspect, see last
month’s tutorial.
by Tim Blythman
For this article, we’re providing a
complete IDE project which you can
open up and use straight away. But it’s
also a great starting point for experimentation, and a wonderful tool for
learning about how FPGAs work (and
about digital logic in general).
Circuit description
Most of the hardware required is on
the pre-built iCEstick module. A small
breakout board that we’ve designed
plugs into the 12-pin PMOD connector,
converting the digital signals from the
iCEstick into signals which are fed to
the VGA connector, to generate VGA
video. The circuit diagram of this
add-on board is shown in Fig.1.
The horizontal synchronisation (HSYNC) and vertical
synchronisation (VSYNC)
lines are effectively fed digital
pulses via series resistors,
which provide a degree of
protection to the FPGA in case of
static electricity and so on.
The red, green and blue VGA signals
are formed by primitive 2-bit DACs,
made using pairs of resistors in a 2:1
ratio, giving four evenly spaced voltage
levels between fully off and fully on.
For example, if pins 3 and 9 of CON1
are held low (0V), then 0V is applied to
pin 2 of CON2, the green signal. If both
these pins are high (3.3V), then pin 2
of CON2 is at 3.3V. If pin 3 of CON1 is
Practical Electronics | May | 2020
Here’s the complete (!) project attached to the iCEstick, which in
turn plugs into a free USB socket.
7
2
8
3
9
4
10
5
11
6
12
Fig.1: the circuit for
our VGA Adaptor is
TO iCEstick
delightfully simple,
since so much of the
hard work is done
SC
by reconfiguring the
20 1 9
blocks inside the FPGA.
Three resistive two-bit DACs are formed
by the 1.1kand 560resistors, to
control the red, green and blue voltage
levels on the VGA connector. The HSYNC
and VSYNC pulses are digital signals
fed straight to the VGA socket, with 68
series resistors for safety. The unusual
pin numbering of CON1 is to match the
numbering on the iCEstick; they are
treated as two side-by-side SIL headers,
even though it’s physically a DIL header.
CON1
6
VGA OUTPUT
CON2
560
RED
1.1k
GREEN
560
BLUE
1.1k
560
6
1
7
2
8
3
9
4
10
5
1.1kΩ
11
12
13
HSYNC
14
VSYNC
15
2x
68
iCEstick
VGAVGA
Adaptor
icestick
ADAPTOR
Practical Electronics | May | 2020
1
2 3 4
560Ω
CON2
VGA
out
68Ω
CON1
Generating the clocks
The iCEstick only has a 12MHz oscillator, so we need to use the iCE40HX-1k’s
phase-locked loop (PLL) to bring that
up to around 25MHz. We are generating
a 100.5MHz signal, which we divide
by four to get 25.125MHz. This is the
closest the PLL can get to our target
frequency of 25.2MHz, using a 12MHz
source. This small error doesn’t end up
causing any problems.
This clock signal is divided by a
factor of 800 in the FPGA, to give a
31.4kHz line clock, then again by 525
to give the frame/screen refresh clock
of 59.8Hz. That’s 0.2Hz slower than our
target of 60Hz, but it isn’t uncommon
to see video refresh rates that are not
an exact number of hertz, and virtually
all monitors will handle this.
We have created a 10-bit horizontal pixel counter in the FPGA which
starts at zero and counts up to 799,
incrementing on each clock pulse (at
25.125MHz), then resets back to zero.
Each time it resets, the 10-bit vertical
pixel counter is incremented, and it’s
reset to zero as soon as it exceeds 524.
1.1kΩ
1.1k
1
the VSYNC pin is pulsed every vertical
scan (ending a frame).
560Ω
7
1
(under)
to
iCEstick
1.1kΩ
560Ω
68Ω
Operating principles
We’re generating a 640 × 480 pixel
VGA signal, which involves scanning
800 × 525 pixels. The extra pixels are
hidden in black borders outside the
normal display area of the screen (in
the front/back porch and rescan areas).
For a standard 60Hz monitor update
rate, we need a 25.2MHz pixel clock
(800 × 525 × 60Hz).
Our alphanumeric terminal occupies a central 512 × 384 pixel region
of the 640 × 480 display image, with
black borders around the edge. We’ve
done this because the 512 × 384 pixel
region maps to 32 × 16 alphanumeric
characters, and 32 × 16 = 512 which
is the number of bytes in each block
of RAM within the FPGA.
It would be possible to combine
multiple RAM blocks to allow a larger
character display (possibly filling the
screen), but that would complicate
the code design somewhat. Making
the relevant changes could be a good
exercise for readers who really want to
delve into FPGA programming.
The FPGA scans the 800 × 525 area,
uses its video RAM to determine which
character should be displayed at any
given point, then uses the font ROM
to determine whether the foreground
or background colour should be displayed for each pixel. The HSYNC
signal is pulsed at the end of each
horizontal scan (ending a line), and
12
high (3.3V) and pin 9 is low (0V) then
the voltage divider formed by the 560Ω
and 1.1kΩ resistors means that around
2.2V is fed to pin 2 of CON2, while if
those voltages are reversed, pin 2 is at
around 1.1V.
By using various combinations of
levels on the red, green and blue lines,
we can generate 4 × 4 × 4 = 64 different
colours on the screen.
The 560Ω and 1.1kΩ resistor values
have been chosen to avoid exceeding
the FPGA’s 8mA per pin sink/source
current rating. We found that on some
monitors, this resulted in colours
which were a bit dark, so you may
wish to try slightly lower values (eg,
470Ω and 910Ω or 430Ω and 820Ω).
If you are unsure, stick with the suggested values.
All timing and signal generation is
done within the FPGA. We won’t claim
the output is fully VGA compliant, but
we have had no troubles using it with a
few different monitors we tested.
SC
20 1 9
Fig.2: fit the resistors to the PCB as
shown here, then the VGA socket,
which goes on the same side as
the resistors, and finally the 6 × 2
pin header, on the opposite side.
The resulting assembly then plugs
straight into the iCEstick and a
standard VGA monitor cable.
5
6 7 8 9 10
11 12 13 14 15
VGA socket – looking at pins.
31
Screen1: this is a broad map of the functional parts of the ‘iCEstick VGA Terminal.ice’ project. You will need to install
IceStudio, download and open that file and zoom in to see the detail of each block.
So these two counters allow us to
keep track of which pixel is currently
being emitted.
When these pixel values are within
the 512 × 384 active area in the middle
of the screen, they are further divided
by 16 (horizontal) and 24 (vertical) to
determine which character position is
currently being displayed. The FPGA
then retrieves the 8-bit ASCII character
value and two 4-bit colour (background/
foreground) values from its video RAM.
The 4-bit colour values are then
used to look up the 16-entry palette
to determine the 6-bit colour values
to use as the foreground and the
background for the character currently
being emitted. Similarly, the ASCII
character value is used to look up an
entry in the font ROM.
All these lookups culminate in a
pixel colour value which is then fed to
the RGB outputs (pins 2-4 and 8-10).
At the same time, the HSYNC (pin 1)
and VSYNC (pin 7) lines are driven
based on the horizontal and vertical
counters, to generate the required sync
pulses for the monitor.
In more detail, when the horizontal
pixel counter is between 0-511, that
is the active part of the display, and
the RGB outputs are driven. The rest
of the time, the RGB outputs remain
low, so the front porch, back porch and
borders are black. When the horizontal
counter is between 592 and 688, pin 7
is set high, creating the HSYNC pulse.
Similarly, the vertical (line) pixel
clock counts from zero to 524, with
the RGB outputs active from 0-383,
and VSYNC is driven high on lines
443-445. The lines between 446 and
524 are the vertical refresh period, so
the RGB outputs remain low.
Fig.3: this shows
how font glyphs
are converted into
bitmap values, by
adding the value
of the pixels that
should show the
foreground colour.
IceStudio expects
hexadecimal
numbers in the font
ROM, so you will
need to convert
the decimal sums
to hexadecimal
format (eg, using
a ‘programmer’s
calculator’).
32
These sync values have been chosen
by trial and error, to centre the display
on our test monitor.
They can be changed in the FPGA
configuration to adjust the location
on your monitor, although the differences between the values should
remain the same to maintain the same
sync pulse widths.
Implementing this in the FPGA
We’re using the IceStudio software to
demonstrate what can be done using
this software, and while the IceStudio
project looks quite complicated, it can
be broken down into small, easy-tounderstand functional blocks that each
accomplish one small task.
We hope this gives you an insight
into just how easy it is to jump into
creating your own FPGA projects
with Verilog inside IceStudio; do
keep in mind as you work with FPGAs that the outcome is actually an
arrangement of logic gates and flipflops that all work simultaneously,
rather than code that is processed
in sequence.
Screen1 shows the IceStudio project in its entirety. The FPGA is configured by connecting various blocks
together, and we’ve labelled groups
of blocks to indicate their purpose.
If you want to examine the design in
more detail as we explain what each
block does, skip to the section below
titled ‘Installing the software on your
computer’, then come back and read
the following description.
Practical Electronics | May | 2020
Screen2: if you have successfully built the hardware and
programmed the FPGA, you will be greeted by this display
on your VGA monitor.
Clock generation is performed by the
area marked PLL in the project window. The code in this block contains
synthesiser directives that describe
how to configure the PLL. The iCEcube2 software that we reviewed in last
month’s Part 1 is capable of generating
PLL configuration data if you want to
experiment with this block.
To the right of the PLL block and left
of the HSYNC/VSYNC sections are the
clock dividers.
The small blue block divides the
100.5MHz clock by four to give the
25.125MHz pixel clock, which is then
divided by 800 to give the line clock.
This is in turn divided by 525 to give
the frame clock.
The ACTIVE VIDEO DETECT section
compares the pixel and line clocks to
the fixed values indicated above, producing two outputs which are both
high when the current pixel is part of
the 512 × 384 pixel active area. These
are fed to the colour decoder, which
generates black unless both (horizontal
and vertical) active video bits are set.
Below ACTIVE VIDEO DETECT is a
section which divides the pixel count
by two to create indexes for the font
ROM. The line count is effectively
divided by three, to create the vertical
font index. But rather than using a divider, which would be quite large and
complex to implement in the FPGA,
instead, a separate counter is used,
which is only incremented on every
third pulse from the line clock, then
reset when it reaches 128 (ie, 384 ÷ 3).
Serial data handling
The FPGA needs to accept serial data
from the host, both for configuration
and to update the displayed characters and/or colours. The UART block
is shown to the left of the PLL in
Screen1. This is made using opensource Verilog code that is available
at. https://github.com/cyrozap/osdvu,
which also includes a description of
how to interface to it.
We don’t need to send any data
back to the host, so we removed the
Practical Electronics | May | 2020
Screen3: in the window that appears after clicking View
→ Command Output, the folder containing the generated
Verilog file is visible (highlighted section). Open this folder
and find the file named ‘main.v’, which is the generated
Verilog equivalent of the IceStudio project.
transmit-specific sections, to save
FPGA resources.
While a microcontroller would wait
and then branch to code to read from a
buffer when the host is sending data to
it, the FPGA is always ready to react,
and the data from the UART is put into
the video RAM within nanoseconds of
it arriving, simultaneously with the
video output tasks occurring elsewhere
on the chip.
The UART decoder filters the incoming serial data and also holds a video
RAM pointer. This filtering checks the
three high-order bits of each received
character. If all of these are low, then
the received serial data ASCII value
is less than 32. That means that it is
a control byte and processed as such.
The control bytes work as follows.
A carriage return (code 13) causes the
video RAM pointer to be reset to the
start of its line by ANDing its value
with 32. A line feed (code 10) moves
the pointer to the next line by adding
32, and a form feed (code 12) moves the
cursor to position zero by resetting the
pointer, as though starting a new page.
If the received data value is 32 or
higher (and thus an ASCII character),
the received character is written to the
video RAM at a position corresponding
to the pointer’s value and the pointer
is incremented by one. Thus, characters received consecutively appear at
consecutive locations on the display.
The currently selected foreground
and background colour combination is
also written to a separate RAM which
is used to later decode the colour data
for display.
Other control codes are decoded by
the small block to the left of the colour
decoder. Code 14 sets a flag so that the
background colour is set, while code
15 sets the flag to the foreground. If a
value from 16 to 31 is received, it is
sent to the foreground or background
register per the flag.
Because all except the lower four
bits are ignored here, code 16 selects
colour 0 and code 31 selects colour 15
from the palette.
Screen6 shows the default palette of
colours that are available.
The video RAM section takes an
address value made from combining
parts of the horizontal and vertical
‘scan’ position. The small code box on
the left just combines the bits to create
a linear address.
Video RAM
The larger box is the video RAM itself.
This has been coded in a specific way
to use part of the iCE40HX-1k’s BRAM
(block RAM). If not done in quite the
right way, the memory is synthesised
from flip-flops instead of using the
dedicated block RAM. This alternative
is a very poor use of resources; as an
experiment, we tried this, and found
that the 512 bytes of RAM took about
half of the FPGA’s flip-flop resource.
The iCE40HX-1k contains 16 separate 512-byte blocks of double-ported
RAM. The double-port feature means
that it can be read and written at the
same time, which is essential in our
application because we may be trying
to change the display at the same time
that the VGA display logic is reading
from it (as it is reading video RAM
almost constantly).
The small beige block above the
video RAM initialises it at startup to
display some splash-screen text. If you
Parts list
iCEstick VGA Terminal
1 Lattice iCEstick FPGA development
board
[Mouser 842-ICE40HX1KSTICKEV,
Digikey 220-2656-ND]
1 double-sided PCB, code 02103191,
49.5 x 32mm [PE PCB Service]
1 2x6 male pin header (CON1)
1 DE-15 (or HD-15) high-density 15pin female D-connector (CON2)
[AMP 1-1734530-1,
MULTICOMP SPC15430]
Resistors (all 1/4W 1% metal film)
3 1.1kΩ
3 560Ω
2 68Ω
33
Screen4: the default character map/font for the iCEstick VGA
Terminal. It can be changed by editing the font ROM blocks.
The first three lines are the standard ASCII characters at
positions 32-127, followed by some pseudo-graphics characters that can be used to draw boxes, bar graphs and so on.
don’t want this, replace the contents
of the beige block with zeroes, or your
own hexadecimal values for a custom
splash screen.
The small block to the right of the
video RAM generates an address into
the font ROM, based on the displayed
character value from video RAM and
the vertical position of the scan.
Font ROM
The font ROM consists of three BRAM
blocks, each fed by a separate initialising block. While implemented using
RAM blocks, there are no connections
to the write lines, so they remain
unchanged as long as the FPGA is
powered, and are effectively read only.
By using multiple BRAM blocks
and a four-way multiplexer, we can
overcome the 512-byte limit of each
block (hint: this might be a good way
to expand the video RAM). Each BRAM
block encodes 64 characters in eight
bytes each, for a total of 512 bytes.
There’s room to add a fourth BRAM
block below, but we only need 192
characters in the font ROM, so we have
not done so.
Each byte of the font ROM encodes
a horizontal line of eight pixels as a
bitmap. The small block next to the font
ROM decodes the horizontal character
sub-position into a single bit; it is effectively an eight-to-one multiplexer.
The output of the font ROM is a single
bit indicating whether the foreground
or background should be displayed for
the current pixel.
Colour
In a similar fashion to the way the
video RAM is read, data is read from
the colour RAM to determine the combination of foreground and background
colours to be displayed at the current
scan position. This is a separate RAM
block that uses the same address and
clock lines as video RAM as its input.
34
The output of the colour RAM is fed
to the colour decoder.
The colour decoder has a pair of
small 6 × 16 bit ROMs, which are
initialised by the beige blocks above
them. These decode the 4-bit colour
palette index value into the necessary output pin states to generate that
specific colour in the palette. The two
blocks are identical; one is used to
decode the foreground colour and one
the background colour, to simplify the
following logic.
The colours have been chosen based
on those used in the venerable Commodore 64, which also had a 16-colour
display.
To the right of the ROMs are a
row of multiplexers, one for each
output pin involved in driving the
VGA colour lines. The multiplexer
chooses between the foreground and
background colours according to the
line from the font ROM.
This is followed by an AND gate.
The data from the multiplexer is
ANDed with a bit that indicates if the
current scan position is inside the central 512 × 384 pixel box, in which case
the foreground or background colour is
produced. Otherwise, the result is low,
so the outputs of all the AND gates are
low and therefore all the output pins
are low and black is displayed.
Finally, a D-flipflop is used to buffer
this signal into the output pins, so that
their states only change on the pixel
clock. This ensures our pixels are
not subject to jitter and thus line up
squarely on the screen. The result is a
very stable display.
Screen5: the RX (receive) pin of the
UART module can be changed using
this drop-down box. Not all pins in
the list can be used; for example, we
are already using all the PMOD pins
for the VGA output.
Installing the software on your
computer
To build this project, you need to install
the IceStudio integrated development
environment (IDE) software. There are
versions available for Linux, Windows
and macOS. If reading last month’s Part
1, an FPGA tutorial, then you may have
already installed it. Otherwise, follow
the installation instructions at: https://
github.com/FPGAwars/icestudio
We used version 0.4.0. Once installed, you should also install the
toolchain and enable the driver for the
iCEstick (only needed on Windows).
If you’re unsure how to do this, read
the aforementioned tutorial, which
explains this in detail, or read the
IceStudio documentation at: https://
icestudio.readthedocs.io/en/latest/
Now download the iCEstick VGA
Terminal.ice file from the May 2020
page of the PE website and open it
in IceStudio. You will see something
similar to what’s shown in Screen1,
and you can now examine the blocks
in closer detail.
If you need to change any of the
configuration parameters, such as the
serial port baud rate, these are ‘hard
coded’ into the project, so you will
need to change the .ice file using the
IDE graphical interface.
Other settings that can be changed
include the colour palette and font
glyphs. Details on how to change all
these parameters are given below.
By default, the serial interface is
connected to iCEstick’s USB/serial
Practical Electronics | May | 2020
Screen6: these
are the colour
combinations that
can be displayed
by the iCEstick
VGA Terminal.
The characters
shown at each
column are
combined with
the Control key
to create the
foreground colour
shown in many
serial terminal
programs. These
16 colours are
selected from a
set of 64 possible
colours – you
can modify the
ROM values in
the IceStudio
project to choose
different ones.
converter IC, but this could be remapped to an external I/O pin for interfacing with a microcontroller such as an
Arduino board or MicroMite.
Construction
As you can see from the PCB overlay
diagram in Fig.2, there are few components on the board so it shouldn’t
take long to build. The board is coded
02103191, measures 49.5 × 32mm, and
is available from the PE PCB Service.
Start by fitting the two 68resistors;
these are closest to CON2. Bend the legs
at right angles, put through the holes
and splay the legs to hold in place.
Solder and trim the leads just above the
solder fillet on the reverse of the board.
Fit the 560Ω and 1.1kΩ resistors using
a similar procedure.
Now mount the VGA socket next,
ensuring it is properly seated and
flush with the PCB. Solder the larger
mechanical pins, turning up your soldering iron temperature if necessary.
Do ensure you solder the fine pins
of the signal lines carefully to avoid
bridging adjacent pins. You may find
it easier to work with the centre row
of pins first, and check that they are
soldered and tidy before accessing
the outer rows, which have more surrounding space to work with.
Finally, fit the 2 × 6 pin header. This
sits underneath the PCB, on the opposite side to the other components, and
is soldered from the top.
Avoid excessive heat, as this may
melt the plastic shroud, putting the
pins out of alignment.
Check that there are no solder bridges or short circuits, and plug the board
into the PMOD header of the iCEstick.
The VGA socket faces away from the
USB plug of the iCEstick.
Practical Electronics | May | 2020
Plug a VGA cable from the VGA
socket to a monitor or television.
Building and uploading the code
With the iCEstick VGA Terminal.ice
file open in IceStudio, select the iCEstick from the Select → Board menu.
To synthesise the design, click Tools
→ Build, and when the green ‘Build
done’ message appears, click Tools
→ Upload. The keyboard shortcuts
for these commands are Ctrl-B and
Ctrl-U respectively.
Now connect a VGA monitor and
check to see if you have a display,
similar to that shown in Screen2. If
you have a terminal program installed,
such as TeraTerm, PuTTY or even the
Arduino IDE, figure out which serial
port the iCEstick is using and open a
connection to it at 9600 baud with eight
bits, no parity (8-N-1).
Type into the terminal, and you
should see text appear on the screen. If
the Enter key generates a CR/LF pair in
your terminal program, pressing Enter
should cause subsequent text to appear
at the start of the next line. Assuming
your terminal program supports control
codes (which most do, except for the
Arduino IDE), you can change the colours by using control key combinations.
ASCII control codes 1-26 correspond
to pressing Ctrl and one of the letters A-Z on the keyboard; as A is the
first letter of the alphabet, Ctrl-A
sends control code 1. Press Ctrl-N or
Ctrl-O to switch between setting the
foreground or background colour, and
press Ctrl-P through to Ctrl-Z to
change that colour.
The five remaining codes between 27
and 31 map to a combination of Ctrl
plus another key, those keys being: [,
\, ], ^ and _, respectively.
You can now use the iCEstick VGA
Terminal as-is, or you may wish to
experiment further to see what is possible with IceStudio.
Debugging the project
While the block-and-wire methodology of creating a design does not leave
much opportunity for errors, manually
entered code blocks certainly could be
erroneous, and thus can cause a build
error. In this case, you will see a red
bar appear instead of ‘Build done’ after
attempting a build.
It’s possible to view the entire Verilog file that is generated during the
build process. IceStudio converts the
graphical design into a text-based (Verilog) HDL file, then builds that into the
binary bitstream. That’s an intensive
process which involves figuring out
which FPGA resources can be used to
create the required logic and how they
should ideally be interconnected, so it
can take some time.
To view the generated Verilog, click
on the View → Command Output menu
option and open the folder shown
in Screen3. In this folder, there is a
main.v file, which is the Verilog code
that IceStudio has generated.
If you do get a build error, scroll
towards the bottom of the Command
Output window and you should see
an error message indicating the line
number on which the error occurred
(and the nature of the error). This,
in combination with the generated
Verilog, should help lead you to the
source of the error.
It’s a good idea to open main.v in a
text editor which displays line numbers. When building the project, you
may also see some warnings; most
warnings can safely be ignored.
Verilog code blocks
Now that you know how to debug the
code, you may wish to dabble with
the Verilog inside the code blocks in
this project. Here are some tips to get
you started; but don’t think this is the
complete book on Verilog coding! Like
the C language, all statements end with
a semi-colon.
Some lines in the Verilog code are
direct assignments, such as the following used to generate the HSYNC signal.
These generate simple digital logic:
assign out = ((count < stop) &&
(count >= start)) ? 1 : 0;
Here, the ternary operator (? :) assigns
the ‘out’ register the value of 1 (high) if
the value of ‘count’ is less than ‘stop’
and equal to or greater than ‘start’.
Otherwise, ‘out’ is assigned the value
0 (low).
35
The VGA Adaptor simply plugs into
the matching socket on the iCEstick
PCB, while the socket at left connects
to the VGA screen/monitor.
This could pass for valid C code,
apart from the ‘assign’ keyword, but
it should be remembered that we are
synthesising hardware in the form
of logic gates rather than compiling
machine code.
At a few places in this project, we
want to increment a counter based on
an input, for example:
reg [9:0] counter = 0;
always <at>(posedge clk)
begin
counter <= (counter ==
div - 1) ? 0 : counter + 1;
clkout <= (counter <
div/2) ? 1 : 0;
end
This code is in the block that divides
the pixel clock down to a line clock.
The first line specifies that ‘counter’ is
a 10-bit register, and is set to zero on
power-up.
The second line indicates that the
following sequence will only occur on
the positive edge of the clk signal. The
resulting synthesis will use flip-flops to
retain the state of the registers between
clk pulses.
The begin/end statements are used
to group the two following lines so that
they both occur inside the always block.
Here, counter is incremented (ie,
its value is increased by one), unless
it has reached one less than the value
of div, in which case it is reset to zero.
Thus, counter counts from zero to
div-1, which gives us our horizontal
pixel position in the counter register.
The clkout register is set to one
while counter is in the bottom half
of its cycle (less than div/2), and zero
when it is in the top half, thus dividing
the incoming clk signal by the ratio
of div.
This clkout signal is fed into another similar code block, so that every time
the horizontal counter reaches zero, the
vertical counter is incremented. This is
how our raster is generated.
You might note that these registers
are loaded with a <= symbol instead
of a =.
The <= means that they are nonblocking assignments, so they are
considered to occur simultaneously.
If a specific order of assignment is
needed, then the = blocking assignment
operator can be used to enforce
36
this, particularly if the result of one
expression depends on the result of a
previous expression.
The memory block demonstrates a
few other features of Verilog. Using this
specific form of assignment is needed
to enforce the use of block RAM, as
mentioned earlier:
reg [7:0] mem [511:0];
always <at>(posedge wclk) begin
if (write_en)
mem[waddr] <= din;
end
always <at>(posedge rclk) begin
dout <= mem[raddr];
end
initial begin
if (MEM)
$readmemh(MEM,mem);
end
The first line defines an internal register file mem, which has 512 (511-0)
eight-bit (7-0) elements, effectively,
an array. The first always block is
responsible for writing to the memory,
where the 8-bit value din is stored at
position waddr in mem on the positive
edge of wclk, but only if write_en is
high (one).
The second always block performs
a read, loading the value of the memory
(mem) at raddr to the dout register
on the rising edge of rclk. Block RAM
is always synchronous (requiring a
clock) on the iCEstick’s iCE40HX-1k.
Changing the baud rate
We suggested earlier that some of the
features such as baud rate, graphics
and colours can easily be modified.
The baud rate is controlled by a single
value within the UART block.
Around line 26 inside the UART
block is the definition of the CLOCK_
DIVIDE parameter.
You can select 115200 baud by
commenting (adding // to the start
of) this line:
parameter CLOCK_DIVIDE = 312;
and removing the // from the start of:
//parameter CLOCK_DIVIDE = 26;
Note that the CLOCK_DIVIDE value is
determined by dividing 12,000,000 by
four times the baud rate (or 3,000,000
divided by the baud rate) and choosing
the next lower integer.
Choosing the next lower integer
means the baud rate is slightly faster
than desired, but this will handle
receiving a steady stream of characters better than a slightly slower
baud rate.
Changing the font
The font ROM consists of groups of
eight 8-bit hexadecimal values inside
the three FONT blocks. The top-most
block encodes ASCII codes 0-63, the
second 64-127, and the third 128-191.
The most-significant bit is at left,
and the least-significant bit at right,
with the data in rows in order from
top to bottom.
Refer to Fig.3, which shows how
the letter ‘A’ is encoded (it is found at
addresses 0x08 to 0x0F near the top of
the second font ROM block). The whole
font is shown in Screen4.
Changing the colours
The colours are formed by a similar bitmap, with 16 6-bit hexadecimal entries.
The two most-significant bits are for
the blue level, the middle two bits for the
green, and the bottom two bits for red.
Thus 0x00 is black and 0x3F is
white, as per the first two entries, with
the third entry 0x03 being red.
Because the foreground and background colours are stored in separate
ROMs, you could provide different
colour maps for each, but that might
be a bit confusing to use.
I/O pin assignments
Finally, you may wish to map the serial
data to a different pin, so you aren’t
using the USB/serial converter. This
is done by changing the pin connected
to the ‘rx’ input of the UART module,
as shown in Screen5.
We recommend using the ‘TR’ or
‘BR’ groups of pins for I/O; these are the
rows of solder pads along the edges of
the board. (Refer to the iCEstick manual
to check which pin is which.)
Take care as the pins are only rated
for 3.3V I/O levels, so directly connecting a 5V microcontroller is not
recommended, and a level converter
or voltage divider should definitely be
used in that case.
Reproduced by arrangement with
SILICON CHIP magazine 2020.
www.siliconchip.com.au
Practical Electronics | May | 2020
|