-
RAM timing problems (SOLVED)
Posted by
kpow8050
on 05 Apr, 2022 13:39
-
I have just started using Block RAM in my FPGA project, and so far I've been able to implement it in a crude way however I am not getting the results I expect. I have a fixed size block RAM which I am writing data to synchronously each clock cycle, and then reading out that data continuously on the next clock cycles. The RAM has 8 bit wide input and 1 bit output. I have added a delay between read and write, giving at least 2 cycles for each. Despite this, the RAM is behaving with uncertainty in output state, viewing the output on the oscilloscope shows that the RAM will not behave consistently and doesn't reflect the data I am storing in it. I have attached my source code below in case anyone is able to spot an obvious flaw in my implementation
module LaserControl (
input data_in,
input comms_clk,
input execute,
input clk_in,
input in_a,
input in_b,
output reg ld_out);
// Variable and memory constants
parameter bit USE_NEG_EDGE = 1 ;
parameter N_BITS = 16; // Total number of bits supported by the incoming register
parameter N_DATA_BITS = 12; // How many bits of data can be recived in one block
parameter MAX_RAM = 4095;
parameter VRAM_BANK2 = 4095; // Start address for bank 2 of vram
// Control constants
parameter SET_PIXEL_OFFSET = 1;
parameter SET_PIXEL_PERIOD = 5;
parameter SET_SCAN_LINE_LIMIT = 6;
parameter STORE_PIXEL_PACKET = 2; // Memory address is automatically incremented
parameter RESET_ADDRESS = 3;
parameter UPDATE_VRAM = 4;
parameter BLANK_BEAM = 7;
reg reset = 1 ; // since you do not have a reset, we are artificially making preset to on.
reg enable_laser = 0 ; // A reg to block the laser from coming on.
reg laser_out = 0 ;
reg debug_mode = 1 ; // 1 = display test pattern, 0 = display pattern from VRAM
reg [4:0] function_pos = 0 ; // Create a program position pointer which can serve 32 states.
reg [4:0] ram_state = 0 ; // Store the state of RAM operation
reg [4:0] ram_readout_state = 0 ;
reg reg_data_in = 0 ; // Register inputs
reg reg_comms_clk = 0 ;
reg reg_execute = 0 ;
reg reg_in_a = 0 ;
reg reg_in_b = 0 ;
reg sync_in = 0 ;
// Internal registers for keeping track of state
reg [N_DATA_BITS-1:0] max_pixel_count = 50;
reg [N_DATA_BITS-1:0] pixel_offset = 40; // how many pixels to delay the output laser pulses
reg [N_DATA_BITS-1:0] pixel_count = 0 ;
reg [N_DATA_BITS-1:0] pixel_period = 50 ; // Period of the pixel clock (1 = period of main clock, 2 = 1/2 of main clock, etc..
reg [N_DATA_BITS-1:0] pixel_period_timer_reg = 0;
reg sync_in_dly = 0 ;
// communcations
reg comms_clk_dly = 0 ;
reg shift_bit_in = 0 ;
reg [N_BITS-1:0] command_packet = 0 ;
reg [2:0] command_reg = 0 ;
reg [N_DATA_BITS-1:0] data_reg = 0 ;
reg [N_DATA_BITS-1:0] data_reg_reversed = 0 ;
wire sync_pos_trigger = sync_in && !sync_in_dly ;
wire sync_neg_trigger = !sync_in && sync_in_dly ;
wire sync_trigger_on = USE_NEG_EDGE ? sync_neg_trigger : sync_pos_trigger ; // Use a parameter or reg to select the trigger edge.
// Video memory (can store up to 4095 pixels based on the current 12 bit address size)
reg writeEnable = 0; // Write enable for the VRAM
reg [7:0] dataWord = 0; // Data word to write to VRAM
reg VRAM_out = 0; // The output bit of the VRAM
reg [12:0] VRAM_raddress = 0; // Read address for the VRAM
reg [8:0] VRAM_waddress = 0; // Write address for the VRAM
reg [12:0] VRAM_offset = 0; // Offset address for VRAM (for reading bank 1 or bank 2)
VRAM activeRAM ( .clock (clk_in),
.data (dataWord),
.rdaddress (VRAM_raddress),
.wraddress (VRAM_waddress),
.wren (writeEnable),
.q (VRAM_out)); // Current VRAM for outputing pixel data
// Reverse data on the input data register
assign data_reg_reversed[0] = data_reg[11];
assign data_reg_reversed[1] = data_reg[10];
assign data_reg_reversed[2] = data_reg[9];
assign data_reg_reversed[3] = data_reg[8];
assign data_reg_reversed[4] = data_reg[7];
assign data_reg_reversed[5] = data_reg[6];
assign data_reg_reversed[6] = data_reg[5];
assign data_reg_reversed[7] = data_reg[4];
assign data_reg_reversed[8] = data_reg[3];
assign data_reg_reversed[9] = data_reg[2];
assign data_reg_reversed[10] = data_reg[1];
assign data_reg_reversed[11] = data_reg[0];
// **************************************************
// Everything should run from the 25MHz clock.
// **************************************************
always @(posedge clk_in) begin
// Cleanly register FPGA inputs.
reg_data_in <= data_in ;
reg_comms_clk <= comms_clk ;
reg_execute <= execute ;
reg_in_a <= in_a ;
reg_in_b <= in_b ;
// **************************************************
// During the reset condition, do these things.
// **************************************************
if (reset) begin
reset <= 0 ; // dispixel_period_timer_regable the reset.
enable_laser <= 0 ; // Force laser off.
ld_out <= 0 ; // Force laser off.
function_pos <= 0 ; // Clear the function position so that position 0 runs once the reset is released.
end else begin
// **************************************************
// Once the reset condition has cleared, do these things.
// **************************************************
ld_out <= !(enable_laser && laser_out) ; // For now, always drive the laser output pin when these are high and !reset.
// We will change this to a memory lookup when we create the 2048 pixel line memory
// **************************************************
// Communications
// **************************************************
comms_clk_dly <= reg_comms_clk;
// load a bit into the communications register every time the comms clock goes low to high
shift_bit_in <= reg_comms_clk && !comms_clk_dly;
if ( shift_bit_in ) command_packet[N_BITS-1:0] <= { command_packet[N_BITS-2:0] , reg_data_in } ;
// store results in the setting register bank
if (reg_execute) begin
command_reg <= command_packet[2:0]; // Store the command op-code
data_reg <= command_packet[N_BITS-1:N_BITS-N_DATA_BITS]; // Store the data
case (command_reg)
SET_PIXEL_OFFSET : begin // Set the pixel offset register
pixel_offset <= data_reg_reversed;
end
SET_PIXEL_PERIOD : begin // Set pixel period register
pixel_period <= data_reg_reversed;
end
SET_SCAN_LINE_LIMIT : begin // Set scan line limit register
max_pixel_count <= data_reg_reversed;
end
BLANK_BEAM : begin // Blank or unblank the laser beam, also controls debug mode
enable_laser <= data_reg_reversed[0];
debug_mode <= data_reg_reversed[1];
end
STORE_PIXEL_PACKET : begin // Store the pixels in the buffer RAM
if(ram_state == 0) ram_state <= 1; // Initiate store cycle
end
RESET_ADDRESS : begin // reset the buffer address for buffer memory access
if (data_reg_reversed < MAX_RAM) VRAM_waddress <= data_reg_reversed; // 0 will reset it to 0, otherwise the buffer address can be set to an arbitrary address location
else VRAM_waddress <= 1'b0;
end
UPDATE_VRAM : begin // Switch active bank of VRAM by changing address offset
if (VRAM_offset == 0) VRAM_offset <= VRAM_BANK2;
else VRAM_offset <= 0;
end
endcase
end
// **************************************************
// Schmitt trigger
// **************************************************
if (reg_in_a && reg_in_b) sync_in <= 1'b1; // Both high, so set.
else if (!(reg_in_a || reg_in_b)) sync_in <= 1'b0; // Both low, so clear, otherwise, hold the last value.
sync_in_dly <= sync_in;
comms_clk_dly <= reg_comms_clk;
// **************************************************
// Begin Sequencer for controlling RAM
// **************************************************
case (ram_state)
default : ram_state <= 0; // Undefined state, reset back to 0
0 : begin // Do nothing
// Ready state
writeEnable <= 0;
end
1 : begin // Store data, setup RAM storage buffer register
writeEnable <= 1;
dataWord <= data_reg_reversed[7:0]; // Store 1 word into VRAM buffer
ram_state <= ram_state + 1'b1; // Go to next function state (on next clock cyle)
end
2 : begin // Clock in data to RAM
// Data word is clocked in, increment address
if ( VRAM_waddress < MAX_RAM ) VRAM_waddress <= VRAM_waddress + 1'b1; // Increment buffer address
else VRAM_waddress <= 1'b0; // reset buffer address
ram_state <= 0; // reset state
end
endcase
// **************************************************
// Begin Sequencer driving for the laser output
// **************************************************
case (function_pos)
default : function_pos <= 0 ; // If an undefined position has been reached, goto position 0.
0 : begin // This state begins after the reset has been released
enable_laser <= 0 ; // Force laser off
function_pos <= function_pos + 1'b1 ; // Go to next function (1).
end
1 : begin
SET_laser_clock ( pixel_period, pixel_offset ); // setup the laser clock for a delay (pixel offset delay)
if ( sync_trigger_on ) function_pos <= function_pos + 1'b1 ; // Goto next function when the sync_trigger goes from high to low
end
2 : begin
RUN_laser_clock ( pixel_period ); // counting pixel offset delay
end
3 : begin
enable_laser <= 1 ; // Force laser on
SET_laser_clock ( pixel_period, max_pixel_count ); // Set the number of pixels to display in the scan line
function_pos <= function_pos + 1'b1; // Goto next function
end
4 : begin
RUN_laser_clock ( pixel_period ); // counting laser output pixel clock
if( debug_mode == 1 ) laser_out <= pixel_count[0]; // Laser is toggled based on the LSB of pixel counter
else begin
case (ram_readout_state)
default : ram_readout_state <= 0; // Undefined state, reset back to 0
0 : begin // Wait 1 cycle for RAM ready
laser_out <= 1'b1;
ram_readout_state <= ram_readout_state + 1'b1;
end
1 : begin // Read out VRAM
laser_out <= VRAM_out;
ram_readout_state <= ram_readout_state + 1'b1;
end
2 : begin // Increment the address counter
laser_out <= 1'b1;
VRAM_raddress <= VRAM_raddress + 1'b1 ; // Increment the VRAM read address
ram_readout_state <= 1'b0;
end
endcase;
end
end
5 : begin
// Sweep finished
// Turn laser on
laser_out <= 1'b0;
VRAM_raddress <= VRAM_offset ;// reset the VRAM address
ram_readout_state <= 0;
function_pos <= 1; // Reset function counter
end
endcase
// **************************************************
// End Sequencer for driving the laser output
// **************************************************
end // !reset
end // always @25 MHz clock.
// **************************************************
// TASKS - set laser pixel clock and address pointer.
// **************************************************
task SET_laser_clock(bit [N_DATA_BITS-1:0] period,bit [N_DATA_BITS-1:0] length);
begin
pixel_count <= length;
pixel_period_timer_reg <= period;
end
endtask
// **************************************************
// TASKS - run laser pixel timer and address pointer.
// **************************************************
task RUN_laser_clock(bit [N_DATA_BITS-1:0] period);
begin
if ( pixel_period_timer_reg != 0 ) pixel_period_timer_reg <= pixel_period_timer_reg - 1'b1 ; // decrement the pixel period count
else begin
pixel_period_timer_reg <= period; // reset the period counter
if (pixel_count !=0) pixel_count <= pixel_count - 1'b1 ; // Count down until 0
else function_pos <= function_pos + 1'b1 ; // Once 0, auto-increment the function_pos for the case statement.
end
end
endtask
endmodule
-
#1 Reply
Posted by
Someone
on 05 Apr, 2022 13:49
-
Not showing the ram module when you say the problem is with recovering data from it... look into my crystal ball.
-
#2 Reply
Posted by
BrianHG
on 05 Apr, 2022 14:35
-
Since you are not showing you ram instantiation, we cannot help.
Notes
a) The data written in ram takes 2 clock to changes.
b) When reading ram, depending on settings, the data output is usually delayed 2 clocks behind the sent in read address. IE, Clock cycle 1 latches the read address and passes it to the memory array. Clock cycle 2 latches the read data from the memory array and sends it to the rest of your HDL code.
You can also prepare and set a .mif (memory initialization file) which presets the ram at powerup.
(note when using a .mif, modelsim wants a copy of the .mif file in your main project simulation folder.)
-
#3 Reply
Posted by
kpow8050
on 05 Apr, 2022 23:52
-
Sorry guys, my bad for not including the RAM instantiation

. Here it is below (generated using the MegaFunction wizard)
module VRAM (
clock,
data,
rdaddress,
wraddress,
wren,
q);
input clock;
input [7:0] data;
input [12:0] rdaddress;
input [9:0] wraddress;
input wren;
output [0:0] q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri1 clock;
tri0 wren;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [0:0] sub_wire0;
wire [0:0] q = sub_wire0[0:0];
altsyncram altsyncram_component (
.address_a (wraddress),
.clock0 (clock),
.data_a (data),
.wren_a (wren),
.address_b (rdaddress),
.q_b (sub_wire0),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.data_b (1'b1),
.eccstatus (),
.q_a (),
.rden_a (1'b1),
.rden_b (1'b1),
.wren_b (1'b0));
defparam
altsyncram_component.address_reg_b = "CLOCK0",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone II",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 1024,
altsyncram_component.numwords_b = 8192,
altsyncram_component.operation_mode = "DUAL_PORT",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_b = "CLOCK0",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
altsyncram_component.widthad_a = 10,
altsyncram_component.widthad_b = 13,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 1,
altsyncram_component.width_byteena_a = 1;
endmodule
-
#4 Reply
Posted by
BrianHG
on 06 Apr, 2022 00:25
-
Take a look at these 2 files, add the lines in red:
setup_lc.do:
transcript on
if {[file exists work]} {
vdel -lib work -all
}
vlib work
vmap work work
vlog -sv -work work {VRAM.v}
vlog -sv -work work {LaserControl_v2.sv}
vlog -sv -work work {LaserControl_v2_tb.sv}
# Make Cyclone Megafunctions and PLL available.
vsim -t 1ns -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L work -voptargs="+acc" LaserControl_v2_tb
add wave /LaserControl_v2_tb/*
do run_lc.do
run_lc.do:
vlog -sv -work work {VRAM.v}
vlog -sv -work work {LaserControl_v2.sv}
vlog -sv -work work {LaserControl_v2_tb.sv}
restart -force
run -all
wave cursor active
wave refresh
wave zoom range 0ns 1000ns
view signals
This is assuming that the file you listed above is called 'VRAM.v'.
Also, the VRAM.V file needs to be in the same folder as all your other source files.
Adding the correct 2 red lines means modelsim setup .do batch files should work.
The part in green, ' -L cycloneive_ver ' may need to be changed as it tells modelsim to look for Altera's Cyclone IV-E functions, though altsyncram should be backwards/forwards compatible with Cyclone II.
-
#5 Reply
Posted by
BrianHG
on 06 Apr, 2022 00:31
-
Note that once you got your sim working, and you added all the new nets to your waveform, you will discover a few minor logic bombs in your code.
From there, see what you can fix before we dive deeper into solutions.
Also, I recommend in Quartus making a dummy initialization file for the dualport ram, so you will have dummy contents at powerup to help see what is going on.
-
#6 Reply
Posted by
kpow8050
on 17 Sep, 2022 12:10
-
Hi BrianHG, sorry for the long delay - things came up that prevented me working on this project. I have come back to it now and my first attempt is to get the ModelSim running, however it seems that its not able to instantiate the VRAM module. The error message is shown below
# Loading work.LaserControl_tb
# Loading work.LaserControl
# Loading work.VRAM
# ** Error: (vsim-3033) VRAM.sv(50): Instantiation of 'altsyncram' failed. The design unit was not found.
#
# Region: /LaserControl_tb/UUT/activeRAM
# Searched libraries:
# C:/Users/Keith/Documents/Electronic projects/UV laser lithography/LaserControl_v5/work
# Error loading design
Wondering what would cause this error. All the modules are present in the working directory. I have attached the run_lc.do inside the .zip folder in case you need to look at that
-
#7 Reply
Posted by
BrianHG
on 17 Sep, 2022 13:44
-
You forgot to add to your setup_lc.do:
if {[file exists work]} {
vdel -lib work -all
}
vlib work
vmap work work
vlog -sv -work work {vram.sv}
vlog -sv -work work {LaserControl.sv}
vlog -sv -work work {LaserControl_tb.sv}
# Make Cyclone Megafunctions and PLL available.
vsim -t 1ns -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L work -voptargs="+acc" LaserControl_tb
add wave /LaserControl_tb/*
do run_lc.do
When adding an entire new module, you do need to rerun the setup_lc.do, or, re-run the 'vsim ' command line.
Note that in your run_lc.do, it is common practice to make sure that the 'vlog -sv -work work {LaserControl_tb.sv}' is the last one in the list of 'vlog's. However it still seems to work.
-
#8 Reply
Posted by
kpow8050
on 18 Sep, 2022 00:01
-
That has solved the problem!
After a bit of messing about with various errors I managed to get it to compile this far. Essentially it seems to be giving an error about continuous and procedural assignments. I'm not really sure what this refers to or how to fix it?
# Loading altera_mf_ver.ALTERA_MF_MEMORY_INITIALIZATION
# ** Error: (vsim-3838) LaserControl.sv(92): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(91): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(90): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(89): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(88): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(87): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(86): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(85): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(84): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(83): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(82): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# ** Error: (vsim-3838) LaserControl.sv(81): Variable '/LaserControl_tb/UUT/data_reg_reversed' written by continuous and procedural assignments. See LaserControl.sv(58).
#
# Region: /LaserControl_tb/UUT
# Error loading design
# Error: Error loading design
-
#9 Reply
Posted by
BrianHG
on 18 Sep, 2022 00:10
-
Well, read the line numbers with the errors.
Something register written by continuous at line 58 has also simultaneously procedural assignments on lines 81 through 92.
You can define an assignment at one or the other, but not both.
-
#10 Reply
Posted by
kpow8050
on 18 Sep, 2022 00:46
-
I modified the data_reg_reversed to a wire and the error goes away. I get a new error instead it shows
Error (10028): Can't resolve multiple constant drivers for net "data_reg_reversed[0]" at lasercontrol.sv(81)
Essentially I cannot see what is wrong with this, it is only defined once in the code. Ideally all that is required of these lines is to reverse the data in a register. My assumption is that it would be easier to do on the FPGA side, rather than change the arduino code side. I would prefer this occurs in logic, rather than sequential way too
// Reverse data on the input data register
assign data_reg_reversed[0] = data_reg[11];
assign data_reg_reversed[1] = data_reg[10];
assign data_reg_reversed[2] = data_reg[9];
assign data_reg_reversed[3] = data_reg[8];
assign data_reg_reversed[4] = data_reg[7];
assign data_reg_reversed[5] = data_reg[6];
assign data_reg_reversed[6] = data_reg[5];
assign data_reg_reversed[7] = data_reg[4];
assign data_reg_reversed[8] = data_reg[3];
assign data_reg_reversed[9] = data_reg[2];
assign data_reg_reversed[10] = data_reg[1];
assign data_reg_reversed[11] = data_reg[0];
-
#11 Reply
Posted by
BrianHG
on 18 Sep, 2022 01:02
-
Line 58:
reg [N_DATA_BITS-1:0] data_reg_reversed = 0 ;
You cannot assign a fixed 0, and then assign a new value, it is not being used as clocked logic register which can change values. In fact, what you have there is actually a wire which you are assigning to another register. You should define it as:
wire [N_DATA_BITS-1:0] data_reg_reversed;
Though, the 'reg' without the =0 should still work. It is just better to comprehend how you are using the net name 'data_reg_reversed'.
-
#12 Reply
Posted by
kpow8050
on 18 Sep, 2022 01:16
-
Line 58:
reg [N_DATA_BITS-1:0] data_reg_reversed = 0 ;
You cannot assign a fixed 0, and then assign a new value, it is not being used as clocked logic register which can change values. In fact, what you have there is actually a wire which you are assigning to another register. You should define it as:
wire [N_DATA_BITS-1:0] data_reg_reversed;
Though, the 'reg' without the =0 should still work. It is just better to comprehend how you are using the net name 'data_reg_reversed'.
Perfect. That fixed it! I don't know how I missed that, I must still be stuck in my sequential programming mode
-
#13 Reply
Posted by
BrianHG
on 18 Sep, 2022 01:26
-
I'm not sure, but if you are only using the reversed data, why not change this line 135:
if ( shift_bit_in ) command_packet[N_BITS-1:0] <= { command_packet[N_BITS-2:0] , reg_data_in } ;
to:
if ( shift_bit_in ) command_packet[N_BITS-1:0] <= { reg_data_in, command_packet[N_BITS-1:1] } ;
Though your data comes in reverse, you would carefully need to modify lines 139 & 140 as the location of the data and command is reversed as well as the command ID name/numbers.
Anyways, what you have is fine.
-
#14 Reply
Posted by
kpow8050
on 18 Sep, 2022 03:55
-
Makes sense. I think I will stick with what I have for now.
Now that I am debugging the memory problem, I have decided to create a memory initialization file (.mif) to initialize the contents of the RAM to a defined state. I added the following line into the defparam section of the VRAM.sv file to load the memory contents (attached in the .zip is the .mif file)
"altsyncram_component.init_file = "LaserControl.mif";"
I setup the test bench so that the ld_out will output the contents of memory starting from address 0 and incrementing every time the pixel counter goes up. Currently there is no output from the ld_out, however there should be 0, 1, 0, 1 etc output based on the memory contents I have given it. I'm wondering if there is a way to check if the RAM was initialized properly? Either way the code is not working how I would expect.
-
#15 Reply
Posted by
BrianHG
on 18 Sep, 2022 05:04
-
Since you have the .mif set to 8 bits and the output set to 1bit, shouldn't the output go:
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1...
Remember, you can specify the 'layout' of the .mif file to match the A input port, or the B output port.
But, since you set the .mif to 8 bit, I assume you have the layout on port A.
If you want 0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0..., you will need to make the first address 0 byte = 8'h55, (or 8'hAA, I don't remember the output bit addressing order.), and all other bytes = 0.
-
#16 Reply
Posted by
kpow8050
on 19 Sep, 2022 03:20
-
I was under the impression that it would assume the port A format. But in any case wouldn't the output still result in 0,1,0,1... if it were defined as 8 bit or 1 bit, since when reading from the 1 bit output port each address increment effectively covers each bit in the 1 byte word. Maybe I should just stick with a symmetrical memory structure since i'm not really sure how this is working underneath. I could go with 8 bit in 8 bit out and have a counter to keep track of address and bit number on the output. When I test the design on the real hardware the memory seems to be corrupt or something, because it causes the device to lock up or get into an undefined state whenever I switch it into the memory reading mode. Debugging so far has not revealed the bug to me so I will need to continue to dig deeper on this. Worst case scenario I may go with an external static RAM chip as I'm pretty familiar with how to get those working
-
#17 Reply
Posted by
kpow8050
on 04 Oct, 2022 04:31
-
I have solved the timing issues with the block RAM on the FPGA. Just thought I would post an image of a successful PCB exposure that I did with this device. There is still a few things that need to be tweaked and unknown issues such as distortion at the end of scan lines (not sure if its from the laser or due to packets of data being lost or both). But this project is a success.