Update:
- I can output HDMI video now
- I can now program the FPGA with raspberry pi
- I have finalized the all-logic, two board version of the béchamel and just need a Mouser order to put together the final version.
- After this béchamel 1 experience I have concluded that running a video synth operation developing with discrete logic chips would be so incredibly impractical (expensive, time consuming, physically large, inflexible) compared to working with reprogrammable logic like the FPGA.
- I want to finish this chapter of my project and then take a moment, to stop working, to think and to reflect. I either want to shift my attention to the preparedinstruments site, and trying to manage the making and selling of this board, or to take a break from electronics/programming research to read and explore elsewhere for a bit. I also want to show my project to people, like Vincent Rioux, and get their honest feedback about the meaning of this thing outside of my own interests and learning. I want to revisit my strategies and practices, how I work with lead solder and how I decide what boards to make. For instance, it was cool to design and produce lots of boards but it is inefficient when a single board that is just partially populated can do the work of various boards. Another thing : I have spent all this time testing stuff and have ended up with a board that is the same as everyone else’s : FPGA w/ SRAM, single board, rpi programmable. Should I start by doing things that other people are doing instead of trying to do everything else first ?
Update 2 :
Getting the SD card interfaced with FPGA isn’t a gimmick (like having an integrated screen, for instance) as it would make turn this object into a fully fledged “product” which isn’t reliant on another consumer product (like Arduino or rpi) to function. It makes sense to use an FPGA as Arduino cannot output HDMI, and is limited in terms of its ability to read an SD card, manage an SRAM, listen for buttons, and modify video simoultaneously. The system would be kind of complete with both large memory stockage, fast memory, input management, video output. But would it be fun to sell a product ? I would have to do do customer service, repairs, answer questions on the forum, handle shipping and customs. I think I would rather do art installations and kits / workshops ?
Recent Wintergatan youtube video suggested making explicit these :
- Problem definition : There is a lack of inexpensive, HDMI-compatible, live-playable, lo-fi hardware video synthesis devices on the synth market. But to be honest, this thing is not a response to a problem so much as a gratuitous problem-generating project (a.k.a a creative project).
- Design requirements : HDMI out, video in via SD card, USB-C power, tactile interface, (audio in ?). Live-playable, relatively small and light (to be easily shipped), inexpensive (~25 euros). Minimilist in design : no bloated software, everything implemented as simply and as fundamental/low-level as possible and everything using only 1,200 LUTs and 2MB of SRAM. On the other hand the device should be fun, quirky, expressive and playful in it’s output and physical incarnation. Everything on the board should be solderable by hand, nothing so small or specialized that you need special tools apart from a fine soldering iron.
I am working on a low-cost, NO SCHOOL NEVERS prototype that is just the Villette Makers PEW model with the addition of a small SRAM. This will be fun because I’ll be able to test the HDMI synth with memory and buttons. Before sending this design off to be made, I want to experiment more with SRAM and FPGA (to figure out what the smallest size memory I can get away with and if I should consider adding anything else to this board based on what I learn from these experiments).
- How can I record and playback with sync so images don’t jump around (taking H SYNC or Display Enable information in, presumably) ?
- How can I reduce the resolution of recordings (with clock divisions)?
- How can I record video to a single channel and mix several channels at output (put them on different colors is the easiest) ?
****
I mentioned Nosedrip, I also want to be able to make art like You Are Listening To. There is radio communication from various city systems like the train network, airports, waterways, etc. combined with ambient sounds.
****
Working on slides for NO SCHOOL to explain what an FPGA is. Looking at some possible analogies. I can’t yet totally understand the difference between control flow programming (like in C) which performs one operation at a time (they digest?) and working with data flow pipelines that just kind of react (like a mirror or a lens) because of their form. I can’t tell if microfluidics and parallel marble machines are like FPGAs if you say that fluid moves super fast…
***
Trick to read analog signals with FPGA ( basically time how long it takes to charge a capacitor ) is genius : https://web.archive.org/web/20190615204217/https://hamsterworks.co.nz/mediawiki/index.php/Cheap_Analogue_Input * TO ADD TO NEXT BOARD*
***
In order to test SRAM recording, I’ve got the 4x16MB board back up and running. Here’s how I modified the board to get a test setup running :
- I took out the SD card from the previous RPI that was no longer appearing to output video from DPI. I have inserted it into a fresh raspberry pi and soldered a thinner profile 20×2 header “pi hat” (which makes the board less vulnerable to bending and losing connections).
- I desoldered the third and fourth SRAM chips (C&D) as they were causing issues for mysterious reasons I couldn’t debug. In any case I want to see what I can do with very little memory for the next board so not an issue.
- I replaced the PFET as I noticed before that it was getting hot and also that the power was fading after being initially plugged in.
- I removed the 50MHz oscillator on the board because it was unreliable and my attempts to resolder it just made it worse. So now it works with either rpi pixel clock or an external 25MHz from the function generator.
- I desoldered the two jumper wires I had soldered to access rpi_DEN & another non-connected rpi pin. All references to DEN have also been removed in the verilog.
There are two verilog codes which I’ve named vga_sync.v and vga_sync_test.v that need to be added :
/*
640x480 VGA singal generator
============================
- Creates h_sync,v_sync signals
- Creates display enable signal and horizontal, vertical
pixel position in display (h,v)
*/
`default_nettype none
module vga_sync(
input wire clk_in,
input wire reset,
output reg h_sync,
output reg v_sync,
output wire clk_sys,
output reg [11:0] h_count,
output reg [11:0] v_count,
output reg display_en
);
// Pixel counters
reg [11:0] h_counter = 0;
reg [11:0] v_counter = 0;
/*
//FOR 100MHz
localparam h_pixel_total = 3200;
localparam h_pixel_display = 2560;
localparam h_pixel_front_porch_amount = 64;
localparam h_pixel_sync_amount = 384;
localparam h_pixel_back_porch_amount = 192;
localparam v_pixel_total = 2100;
localparam v_pixel_display = 1920;
localparam v_pixel_front_porch_amount = 40;
localparam v_pixel_sync_amount = 8;
localparam v_pixel_back_porch_amount = 132;
*/
// FOR 50MHz
localparam h_pixel_total = 1600;
localparam h_pixel_display = 1280;
localparam h_pixel_front_porch_amount = 32;
localparam h_pixel_sync_amount = 192;
localparam h_pixel_back_porch_amount = 96;
localparam v_pixel_total = 1050;
localparam v_pixel_display = 960;
localparam v_pixel_front_porch_amount = 20;
localparam v_pixel_sync_amount = 4;
localparam v_pixel_back_porch_amount = 66;
/*
//FOR 25MHz
localparam h_pixel_total = 800;
localparam h_pixel_display = 640;
localparam h_pixel_front_porch_amount = 16;
localparam h_pixel_sync_amount = 96;
localparam h_pixel_back_porch_amount = 48;
localparam v_pixel_total = 525;
localparam v_pixel_display = 480;
localparam v_pixel_front_porch_amount = 10;
localparam v_pixel_sync_amount = 2;
localparam v_pixel_back_porch_amount = 33;
*/
always @(posedge clk_in) begin
if (reset) begin
//Reset counter values
h_counter <= 0;
v_counter <= 0;
display_en <= 0;
end
else
begin
// Generate display enable signal
if (h_counter < h_pixel_display && v_counter < v_pixel_display)
display_en <= 1;
else
display_en <= 0;
//Check if horizontal has arrived to the end
if (h_counter >= h_pixel_total)
begin
h_counter <= 0;
v_counter <= v_counter + 1;
end
else
//horizontal increment pixel value
h_counter <= h_counter + 1;
// check if vertical has arrived to the end
if (v_counter >= v_pixel_total)
v_counter <= 0;
end
end
always @(posedge clk_in) begin
// Check if sync_pulse needs to be created
if (h_counter >= (h_pixel_display + h_pixel_front_porch_amount)
&& h_counter < (h_pixel_display + h_pixel_front_porch_amount + h_pixel_sync_amount) )
h_sync <= 0;
else
h_sync <= 1;
// Check if sync_pulse needs to be created
if (v_counter >= (v_pixel_display + v_pixel_front_porch_amount)
&& v_counter < (v_pixel_display + v_pixel_front_porch_amount + v_pixel_sync_amount) )
v_sync <= 0;
else
v_sync <= 1;
end
// Route h_/v_counter to out
always @ (posedge clk_in) begin
h_count <= h_counter;
v_count <= v_counter;
end
endmodule
*****************************************************************************************************************************
// vga_sync_test verilog code
`default_nettype none
module vga_sync_test(
input wire clk_in,
input wire reset,
input wire rec, // Direction of io, 1 = set output, 0 = read input
//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,
//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,
output wire h_sync,
output wire v_sync,
//SRAM
output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire
output wire cs_1,
output wire cs_0,
output reg we_1,
output reg we_0
);
wire [7:0] data_in;
wire [7:0] data_out;
reg toggle;
reg [7:0] a, b;
assign io = rec ? a : 8'bzzzzzzzz;
assign data_out = b;
assign data_in[1:0] = r_in[3:2];
assign data_in[3:2] = b_in[3:2];
assign data_in[5:4] = g_in[3:2];
assign data_in[7:6] = 2'b00;
wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;
localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;
// CS: low to select, high to deselect
assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;
//SRAM address counter
always @(posedge clk_in) begin
if (addr == 0) begin
toggle <= toggle+1;
end
if (reset) begin
addr <= 0;
end else begin
addr <= addr+1;
end
end
//REC control
always @(posedge clk_in) begin
b <= io;
a <= data_in;
if (rec) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
else begin
we_0 <= 1;
end
end
//VGA COLOR OUT
always @(posedge clk_in) begin
if (display_en) begin
r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];
end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end
vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);
endmodule
set_io v_sync 87
set_io h_sync 88
set_io clk_in 64
set_io reset 66
set_io io[0] 1
set_io io[1] 2
set_io io[2] 3
set_io io[3] 4
set_io io[4] 122
set_io io[5] 121
set_io io[6] 120
set_io io[7] 119
set_io addr[0] 138
set_io addr[1] 139
set_io addr[2] 141
set_io addr[3] 142
set_io addr[4] 143
set_io addr[5] 8
set_io addr[6] 9
set_io addr[7] 10
set_io addr[8] 11
set_io addr[9] 12
set_io addr[10] 136
set_io addr[11] 135
set_io addr[12] 134
set_io addr[13] 129
set_io addr[14] 128
set_io addr[15] 117
set_io addr[16] 116
set_io addr[17] 115
set_io addr[18] 114
set_io addr[19] 137
set_io addr[20] 113
set_io cs_0 144
set_io cs_1 71
set_io we_0 7
set_io we_1 50
set_io rec 62
set_io r_in[0] 107
set_io r_in[1] 106
set_io r_in[2] 105
set_io r_in[3] 104
set_io g_in[0] 97
set_io g_in[1] 96
set_io g_in[2] 95
set_io g_in[3] 112
set_io b_in[0] 102
set_io b_in[1] 101
set_io b_in[2] 99
set_io b_in[3] 98
set_io r_out[0] 81
set_io r_out[1] 80
set_io r_out[2] 79
set_io r_out[3] 78
set_io g_out[0] 94
set_io g_out[1] 93
set_io g_out[2] 91
set_io g_out[3] 90
set_io b_out[0] 76
set_io b_out[1] 75
set_io b_out[2] 74
set_io b_out[3] 73
**************************************************************************************************************************
I describe here how I loaded videos to the rpi and setup the .desktop autorun code :
"Setting up another rpi to loop VLC videos" from https://www.marrs.io/la-bechamel/
Here is the raspberry pi config file I've used to get DPI working:
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details
# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1
# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16
# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720
# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2
# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4
# uncomment for composite PAL
#sdtv_mode=2
#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800
# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=off
#dtparam=i2s=on
dtparam=spi=off
# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18
# Additional overlays and parameters are documented /boot/overlays/README
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
# Automatically load overlays for detected cameras
camera_auto_detect=1
# Automatically load overlays for detected DSI displays
#display_auto_detect=1
# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Disable compensation for displays with overscan
disable_overscan=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
[pi4]
# Run as fast as firmware / board allows
arm_boost=1
[all]
gpio=0-9=a2
gpio=12-17=a2
gpio=20-25=a2
gpio=26-27=a2
dtoverlay=dpi24
enable_dpi_lcd=1
display_default_lcd=1
dpi_group=2
dpi_mode=87
extra_transpose_buffer=2
dpi_output_format=0x7f216
dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 50000000 1
******
- How can I record and playback with sync so images don’t jump around (taking H SYNC or Display Enable information in, presumably) ?
- How can I reduce the resolution of recordings (with clock divisions)?
- How can I record video to a single channel and mix several channels at output (put them on different colors is the easiest) ?
SYNC
I have tried using Data Enable (DEN) from the rpi and inputting that, and also using H_SYNC and V_SYNC from the r_pi. So far the best result is taking V_SYNC, but I am still getting drifting. Testing to see if both H_SYNC and V_SYNC are high isn’t more stable for some reason. Here is that code :
`default_nettype none
module vga_sync_test(
input wire clk_in,
input wire reset,
input wire rec, // Direction of io, 1 = set output, 0 = read input
//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,
input wire DEN,
//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,
output wire h_sync,
output wire v_sync,
//SRAM
output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire
output wire cs_1,
output wire cs_0,
output reg we_1,
output reg we_0
);
wire [7:0] data_in;
wire [7:0] data_out;
reg toggle;
reg [7:0] a, b;
assign io = rec ? a : 8'bzzzzzzzz;
assign data_out = b;
assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;
wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;
localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;
// CS: low to select, high to deselect
assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;
//SRAM address counter
always @(posedge clk_in) begin
if (addr == 0) begin
toggle <= toggle+1;
end
if (reset) begin
addr <= 0;
end else begin
//only increment address is DEN high
if (DEN) begin
addr <= addr+1;
end
end
end
//REC control
always @(posedge clk_in) begin
b <= io;
a <= data_in;
if (rec) begin
//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end
//VGA COLOR OUT
always @(posedge clk_in) begin
//only display pixels if DEN high
if (DEN) begin
r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];
end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end
vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);
endmodule
I am going to try to count V_SYNCs and use this to vary SRAM address incrementing.
I can get an image to freeze pretty well now, I just needed to find the right size of the V_SYNC_COUNT counter to make it work. Here is the code :
`default_nettype none
module vga_sync_test(
input wire clk_in,
input wire reset,
input wire rec, // Direction of io, 1 = set output, 0 = read input
//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,
input wire DEN,
output reg [2:0] V_SYNC_COUNT, // the size of this counter impact how many V_SYNC signals it takes to reset the SRAM ADD counter
//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,
output wire h_sync,
output wire v_sync,
//SRAM
output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire
output wire cs_1,
output wire cs_0,
output reg we_1,
output reg we_0
);
wire [7:0] data_in;
wire [7:0] data_out;
reg toggle;
reg [7:0] a, b;
assign io = rec ? a : 8'bzzzzzzzz;
assign data_out = b;
assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;
wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;
localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;
// CS: low to select, high to deselect
assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;
//V SYNC counter
always @(posedge DEN) begin
V_SYNC_COUNT <= V_SYNC_COUNT+1;
if(reset == 1) begin
V_SYNC_COUNT <=0;
end
end
//SRAM address counter
always @(posedge clk_in) begin
if (addr == 0) begin
toggle <= toggle+1;
end
// reset the address once we've rolled over V_SYNC_COUNT
if (reset | V_SYNC_COUNT == 0 ) begin
addr <= 0;
end else begin
//only increment address is DEN high
if (DEN) begin
addr <= addr+1;
end
end
end
//REC control
always @(posedge clk_in) begin
b <= io;
a <= data_in;
if (rec) begin
//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end
//VGA COLOR OUT
always @(posedge clk_in) begin
//only display pixels if DEN high
if (DEN) begin
r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];
end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end
vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);
endmodule
I just realized I could also take the AND of various bits of the V_SYNC_COUNTER like so :
if (reset | ((V_SYNC_COUNT[0] == 1) & (V_SYNC_COUNT[2] == 1) & (V_SYNC_COUNT[3] == 1))) begin
addr <= 0;
end
RESOLUTION
This is cool, just change clk_in with a divided clock and you get bigger pixels !
`default_nettype none
module vga_sync_test(
input wire clk_in,
output reg [2:0] clk_div, // remember to change clk_div[MSB] also below ! Changing the size of this register changes how big the pixels are :D !
input wire reset,
input wire rec, // Direction of io, 1 = set output, 0 = read input
//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,
input wire DEN,
output reg [1:0] V_SYNC_COUNT,
//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,
output wire h_sync,
output wire v_sync,
//SRAM
output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire
output wire cs_1,
output wire cs_0,
output reg we_1,
output reg we_0
);
wire [7:0] data_in;
wire [7:0] data_out;
reg toggle;
reg [7:0] a, b;
assign io = rec ? a : 8'bzzzzzzzz;
assign data_out = b;
assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;
wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;
localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;
// CS: low to select, high to deselect
assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;
//V SYNC counter
always @(posedge DEN) begin
V_SYNC_COUNT <= V_SYNC_COUNT+1;
if(reset == 1) begin
V_SYNC_COUNT <=0;
end
end
//increment our clock divider
always @(posedge clk_in) begin
clk_div <= clk_div+1;
if(reset == 1) begin
clk_div <=0;
end
end
//SRAM address counter
always @(posedge clk_div[1]) begin
if (addr == 0) begin
toggle <= toggle+1;
end
// reset the address once we've rolled over V_SYNC_COUNT
if (reset | V_SYNC_COUNT == 0 ) begin
addr <= 0;
end else begin
//only increment address if DEN high
if (DEN) begin
addr <= addr+1;
end
end
end
//REC control
always @(posedge clk_div[1]) begin
b <= io;
a <= data_in;
if (rec) begin
//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end
//VGA COLOR OUT
always @(posedge clk_div[1]) begin
//only display pixels if DEN high
if (DEN) begin
r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];
end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end
vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);
endmodule
Considering removing the amps at the output, as the NO SCHOOL version won’t have these either.
I should definitely change the shape of the pixels to make them rectangular in both X and Y.
**********
I might be entering a new career phase as a prototyper. I am currently working to make an e-ink QR code device, a rotating clock mechanism, and may be tasked with making permanent versions of the FLV DSAA prototypes we proposed !
I can imagine a transition where I do more prototyping professionally and spend my time doing more fine art work – less design of circuits and more experimental video making with my existing boards + new code. Routing circuits is so meticulous, I don’t know how I’ve spent so many hours doing this.
**************
I have finalized the next iteration of the PEW board with a small inexpensive 2MB SRAM, an rpi header (which is also hard-wired to the FLASH programming pins), and an SD card set up to work in either 1 or 4 bit mode (not SPI0) to eventually take in uncompressed video directly, and a test of the potentiometer reading capabilities of the FPGA. The music in + comparator has been removed from this version. I also haven’t been able to learn quickly enough a minimal setup for the FTDI chip so that has not been included either.
**************
UPDATE : It appears that SPI mode, not SD 1-bit mode, is the more thoroughly documented way of interfacing SD cards.
Here is where the SD 1-bit mode communication protocol is explained : https://www.sdcard.org/downloads/pls/ and select Physical Layer Simplified Specification where chapter 4 describes in detail.
I think I should aim for the Multiple Block Read Operation (CMD18) which keeps going until you send a stop command. 1 bit mode seems the simplest, everything is just in serial.
Here are some sample diagrams :
*************************
General stuff : CMD is used for sending messages, DAT0 for sending data. 3.3V is the standard. Default clock speed is 400KHz, default RCA is 0x0000. Phase 1 is Card ID mode then Data TX mode. 512 bytes sent at a time. According to this post (http://forum.6502.org/viewtopic.php?f=4&t=7300) data is read on the low-to-high transition, and starts with the highest bit (47, for example) going to the lowest bit. Here is a site to calculate CRC (https://www.lddgo.net/en/encrypt/crc)
Here is the start-up procedure :
**********************
Card ID mode: After power on, the Host sends to the SD card first :
0. CMD0 (enter IDLE mode). (And now pull CMD high and tick the CLK a series of times ?)
- CMD8 (operating condition validation)
2. ACMD41 (SD_SEND_OP_COND)
3. CMD2 (ALL_SEND_CID) : CID is a unique card ID number.
4. CMD3 (SEND_RELATIVE_ADDR) which asks the card to send it’s relative card address (RCA).
*********************************
Now to enter Data TX mode :
5. CMD7 : Puts the card into TX mode.
6. CMD18 : Multiple Block Read (continues until CMD12 STOP_TRANSMISSION)
7. DAT0 bus sends first 1 of 4 bits LOW followed by a continuous data stream synchronized with the CLK.
**************************
FTDI
I am just now thinking that if I am making a product I don’t necessarily need to worry about making it USB programmable. I can just leave the programming header and a link to how to program with Arduino/raspberry pi.
Looking in to FTDI replacements. This post (https://forum.arduino.cc/t/explanation-of-ftdi/267058/4) suggests these :
- Sil Labs CP2102
- Prolific PL2303
- WCH CH340 (Olimex has a board here : https://www.olimex.com/Products/Breadboarding/BB-CH340T/open-source-hardware)
Fabien says that these chips only do UART however.
There are some open JTAG techniques. Another option he suggests is to have a bootloader as is done with the following boards :
- tinyFPGA-BX (Uses the FPGA itself to program the flash chip : https://github.com/tinyfpga/TinyFPGA-Bootloader)
- Fomu (Uses the FPGA itself to program the flash chip : https://github.com/im-tomu/foboot)
- ICE40HX1K-EVB (Uses an Arduino board – olimexino-32u4 – to program the flash chip)
****
Saw on a blog that controlling a USB flash drive with SPI might be easier than an SD card for some reason ? It looks like a state machine is how you make an FPGA behave like a microcontroller for this type of situation.
Perhaps the usual SPI for SD card is the correct choice. Some things I’m looking at :
- https://hackaday.io/project/160928/logs
- https://www.fpga4fun.com/SPI2.html
- https://hackaday.io/project/119133-rops/log/144622-starting-with-verilog-and-spi
- SPI slave : https://github.com/joeferner/fpga-spi/blob/master/spi.v
- ***NANDLAND SPI Master : https://github.com/nandland/spi-master/blob/master/Verilog/source/SPI_Master.v***
- Optimized SPI Master: https://github.com/freecores/tiny_spi/blob/master/rtl/verilog/tiny_spi.v
- https://alchitry.com/serial-peripheral-interface-spi-verilog
The Nandland SPI Master compiles. Looking at these documents now explaining SD card protocol with SPI :
- http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
- https://users.ece.utexas.edu/~valvano/EE345M/view12_SPI_SDC.pdf
SD interfacing with SPI :
HxD is a tool for raw SD card editing. It could help finding the start address of the first file for instance.
I think step 1 would be to try to do this manually (using an SPI library ?) with Arduino and a logic analyzer / oscilloscope to confirm that what I’m trying to do works with the particular card I am using. With screenshots of the communication I could then attempt to replicate this with the FTDI FPGA which has an SD card and LEDs for indicators.
Explaining how to use Arduino SPI library to send individual bytes :
https://docs.arduino.cc/tutorials/generic/introduction-to-the-serial-peripheral-interface/
https://docs.arduino.cc/learn/communication/spi/?_gl=1*4f5308*_ga*MTk5MjE1MjA0My4xNzA1NzQyNDIx*_ga_NEXN8H46L5*MTcxMTEzOTU0Ny4zOS4wLjE3MTExMzk1NTIuMC4wLjY5OTM2NjkyOQ..*_fplc*SVhlSkEzOXkwVlFqc2MlMkZ6ZlU5NTlzYTJteWJnTWZ5eUVlSUFDSlRZSTl3bVJHd3lMWVZiWTNFenpMUjhOOTVFR2drR3lPdU1NR0pSQWluMGRWYWZRM3htUWFBd3JYeFFiViUyQndybE1SZk9VU1JFT0Y0cXpldG5HVnZXbE1RUSUzRCUzRA..
.
****
Looking at enclosures and found these resources :
https://www.protocase.com/blog/2020/02/20/how-to-choose-between-enclosure-styles/
ponoko.com/blog/how-to-make/making-enclosures-for-electronics-with-ponoko/
Here are some experiments with enclosures :
****
Here’s a more precise model of the 3D print option :
****
Trying to output low resolution images from film using ffmpeg (https://www.bannerbear.com/blog/how-to-extract-images-from-a-video-using-ffmpeg/):
ffmpeg -i input.mp4 -s 100x100 -vf fps=1,extractplanes=y %04d.bmp
This outputs 1 frame a second super small in black and white. The input video needs to be in the same directory of the ffmpeg.exe file. Super easy ! I renamed one of the .bmp as a .raw and opened it in photoshop at 100x100 : *** A couple tests using the screen itself as a display with text overlay :