The previous blog about video generation is here.
For the weekend I posted Verilog Programming challenge to change the shape of the square to circle but unfortunately we didn’t got single solution 🙂
I guess you guys also learn yet and this was hard challenge to solve 🙂
In this post I will show you that this is not so hard.
Let's see again the source from the older post, here is where square is generated: if(c_col > l_sq_pos_x && c_col < r_sq_pos_x && c_row > u_sq_pos_y && c_row < d_sq_pos_y) begin //generate blue square vga_r_r <= 0; vga_g_r <= 0; vga_b_r <= 7; end
What this codes does? If the video coordinates are within square we assign some color to VGA output.
This is the part we should modify to draw circle but how?
We will use for this purpose video memory and in this memory we will draw circle then when the video beam is in this area it will draw the content of the video memory.
Let’s define the memory:
reg [19:0] sq_figure [0:19];
This defines 20 registers which are 20 bit wide, so we define 20×20 bit video memory which we will use to draw image on the VGA screen.
We also need two counters which to count XY beam position within the square area where we will draw:
wire [4:0] sq_fig_x; wire [4:0] sq_fig_y; assign sq_fig_x = c_col - l_sq_pos_x; // our figure's x axis when in square boundary assign sq_fig_y = c_row - u_sq_pos_y; // our figure's y axis when in square boundary
This video memory registers must be load with the ball image, we will do this during the reset state where we initialize other registers too:
if(reset == 1) begin //while RESET is high init counters sq_figure[0][19:0] <= 20'b00000000000000000000; sq_figure[1][19:0] <= 20'b00000001111100000000; sq_figure[2][19:0] <= 20'b00000111111111000000; sq_figure[3][19:0] <= 20'b00011111111111110000; sq_figure[4][19:0] <= 20'b00111111111111111000; sq_figure[5][19:0] <= 20'b00111111111111111000; sq_figure[6][19:0] <= 20'b01111111111111111100; sq_figure[7][19:0] <= 20'b01111111111111111100; sq_figure[8][19:0] <= 20'b11111111111111111110; sq_figure[9][19:0] <= 20'b11111111111111111110; sq_figure[10][19:0] <= 20'b11111111111111111110; sq_figure[11][19:0] <= 20'b11111111111111111110; sq_figure[12][19:0] <= 20'b11111111111111111110; sq_figure[13][19:0] <= 20'b01111111111111111100; sq_figure[14][19:0] <= 20'b01111111111111111100; sq_figure[15][19:0] <= 20'b00111111111111111000; sq_figure[16][19:0] <= 20'b00111111111111111000; sq_figure[17][19:0] <= 20'b00011111111111110000; sq_figure[18][19:0] <= 20'b00000111111111000000; sq_figure[19][19:0] <= 20'b00000001111100000000; c_hor <= 0; c_ver <= 0; vga_hs_r <= 1; vga_vs_r <= 0; c_row <= 0; c_col <= 0; end
Now we have loaded our video memory with the image of the ball and we should add code which draw it:
if(c_col > l_sq_pos_x && c_col < r_sq_pos_x && c_row > u_sq_pos_y && c_row < d_sq_pos_y) begin //generate picture from the video memory if(sq_figure[sq_fig_y][sq_fig_x] == 1) begin vga_r_r <= 7; vga_g_r <= 0; vga_b_r <= 7; end else begin vga_r_r <= 0; vga_g_r <= 0; vga_b_r <= 0; end
What we do here? If the video memory is 1 we draw pink dot otherwise black on the screen. Is it really so simple? Let’s compile and see what happens!
Wow it works! You see the picture above.
Let’s copy this code it to example_4.v for further reference.
Now when we have video memory we can change it content and make animations by change dynamically the image inside the video memory.
To do this we have to load the registers with different ‘picture’.
Before we do this first let’s fix something annoying with the keyboard handling which bothers me. When I press up key ball start to move up and I can’t change the direction until it hits the wall.
This is because we commented the code for key release, so the arrow flags are clear press key flag is to hit the wall.
Let’s make this modification:
if(c_row == 1 && c_col == 1) begin //once per video frame if(u_arr) begin if (sq_pos_y > square_size) begin sq_pos_y <= sq_pos_y - 1; end else begin u_arr <= 0; d_arr <= 1; end end; if(d_arr) begin if (sq_pos_y < (v_pixels - 1 - square_size)) begin sq_pos_y <= sq_pos_y + 1; end else begin d_arr <= 0; u_arr <= 1; end end; if(l_arr) begin if (sq_pos_x > square_size) begin sq_pos_x <= sq_pos_x - 1; end else begin l_arr <= 0; r_arr <= 1; end end; if(r_arr) begin if (sq_pos_x < (h_pixels - 1 - square_size)) begin sq_pos_x <= sq_pos_x + 1; end else begin r_arr <= 0; l_arr <= 1; end end; end
Great! Now I can change up down left right direction on the fly while ball is moving, but the ball after a while start moving only diagonally because up down do not change left right direction 🙂
Let’s save current example to example_5.v for further reference and try to modify one more time the code.
Let’s make if ball is moving up but also in any of X direction pressing second time up to make it move stright up and same for other keys.
First we will need to add de-bounce time as once we press the key each frame this means 25 per second key will be scanned .
We will add de-bounce timer:
reg [19:0] arr_timer; // delay between key check
We define 20 bit counter, which will be clocked at 25Mhz and will overflow after 0.041(6) seconds, we will check keys only when this counter oveflow:
arr_timer <= arr_timer + 1; if(arr_timer == 0) begin if(ps2_data_reg_prev == 8'he0) begin //0xE0 means key pressed if(ps2_data_reg == 8'h75) begin if(u_arr == 1) begin u_arr <= 1; //0x75 up key d_arr <= 0; l_arr <= 0; r_arr <= 0; end else begin u_arr <= 1; //0x75 up key d_arr <= 0; end ps2_data_reg <= 0; end if(ps2_data_reg == 8'h6b) begin if(l_arr == 1) begin l_arr <= 1; //0x6B left key r_arr <= 0; u_arr <= 0; d_arr <= 0; end else begin l_arr <= 1; //0x6B left key r_arr <= 0; end ps2_data_reg <= 0; end if(ps2_data_reg == 8'h72) begin if(d_arr == 1) begin d_arr <= 1; //0x72 down key u_arr <= 0; l_arr <= 0; r_arr <= 0; end else begin d_arr <= 1; //0x72 down key u_arr <= 0; end ps2_data_reg <= 0; end if(ps2_data_reg == 8'h74) begin if(r_arr == 1) begin r_arr <= 1; //0x74 right key l_arr <= 0; u_arr <= 0; d_arr <= 0; end else begin r_arr <= 1; //0x74 right key l_arr <= 0; end ps2_data_reg <= 0; end end end
When u_arr is 0 and it is set 1 we just clear d_arr, but if u_arr already has been 1 and we set u_arr again we clear l_arr and r_arr too.
This way if ball moves diagonally up and we press the up key again it will go straight up, same for the other directions too.
Now everything is perfect! Let save it as example_6.v
One last mod: Let’s use arr_timer to dynamically change the video memory, we add this code
if(arr_timer == 0) begin sq_figure[8][19:0] <= sq_figure[8][19:0] ^ 20'b00000001111000000000; sq_figure[9][19:0] <= sq_figure[9][19:0] ^ 20'b00000001111000000000; sq_figure[10][19:0] <= sq_figure[10][19:0] ^ 20'b00000001111000000000; sq_figure[11][19:0] <= sq_figure[11][19:0] ^ 20'b00000001111000000000;
What we do here, each time arr_timer overflow (25 000 000 / 2^20) i.e. each 0.0416 seconds we xor few lines of the video memory and thus make square hole inside the ball.
We compile and see that the ball animation works but too fast. Let’s change the arr_timer to 21 bit and slow town the blinking.
The new code is saved as example_7.v
What we learn so far? How to define memory in Verilog and how to display this memory content at given coordinates.
All changes are now uploaded at GitHub.
What Next?
In the next Tutorial we will use iCE40HX1K-EVB SRAM memory as video memory to generate video with resolution 640×480 pixels 512 colors.
Then we will teach you how to use the 100Mhz  iCE40-ADC and iCE40-DAC.
Using  iCE40HX1K-EVB as 100Msps Logic Analizer for signals with voltage levels from  1.65V to 5.5V using iCE40-DIO and connected to Sigrok Pulseview and how you can sniff CAN, USB, RFID, I2C, I2S, JTAG, MIDI, MODBUS, SPDIF, SPI, and all other 66 decoding protocols which Sigrok supports.
Jul 21, 2016 @ 04:11:58
Olimex writes:
> For the weekend I posted Verilog Programming challenge to change the shape of the square to circle but unfortunately we didn’t got single solution.
Don’t be disappointed. Your iCE40 board and its add-ons haven’t been out long enough yet for many people to have them. It’s early days, and there is a steep learning curve too, as you mentioned.
The potential of FPGAs is huge though, so you might wish to consider building a cheap iCE40 device into some of your future ARM boards as an I/O accelerator. The Beaglebones gain a great amount of interest from having dual PRU RISC processors which act as realtime accelerators for the main Cortex-A8. iCE40 FPGAs could perform a similar role to the BB’s PRUs on your CPU boards, but with the benefit of far greater speed and working equally well with all of the CPU families that you employ.
I expect that such an I/O and special function accelerator would give you a lot of good press since it would be unique to Olimex (for now), and it would expand your community of Verilog programmers as well.
A tiny iCE40 UEXT module without frills and no SRAM would be nice to see as well, as you have a lot of CPU boards that could usefully be back-fitted with one for applications where the iCE40HX1K-EVB is excessive.
Morgaine.
Jul 22, 2017 @ 17:47:14
Hi,
I am trying to use the SDRAM (K6R4016) on the ICE40HX1K-EVB.
1/ Looking at the datasheets of SDRAM chip, the procedure to read seams relative simple (using the “address controlled” mode, i.e. keep nCS, nOE, nUB and nLB to “0” and nWR to “1”)
– step 1: write address to address-bus
– step 2 (at least 13 ns later);
-> read data from dataout
-> put next address on address-bus
However, to write to the DRAM, the datasheet mentions four different modes. As the databus is both input and output, I’m not sure how exactly what is the best way to do this. What mode do you advice?
What procedure or statemachine do you advice to write to the SDRAM bus?
2/ I noticed that when you create threestate ports in verilog in yosys (as needed for the databus to the SRAM chip), you get a warning saying that threestate ports are concidered “not supported” and “experimental” in yosys.
My preference is to use myhdl for this (but verilog is fine as a backup-sollution).
Kristoff
Jul 22, 2017 @ 23:20:45
hum. Typo in my message above:
s/SDRAM/SRAM/g
Mar 05, 2018 @ 02:18:55
I think we can write a verilog code directly for making this ball round. Because in case we need to make it dynamically increase or decrease size it is not possible by just image.