Ok, I've cleaned up my OSD generator, commented on everything and it gives a much better starting point.
We will make changes to make things better and touch up the font into standard 8 bit bytes, but as an introduction, this shows how the basics of how one of Intel's memory function is called and configured.
module osd_generator ( clk, pc_ena, hs_in, vs_in, osd_ena_out, osd_image, hde_out, vde_out, hs_out, vs_out,
wren_disp, wren_font, wr_addr, wr_data );
// To write contents into the display and font memories, the wr_addr[15:0] selects the address
// the wr_data[7:0] contains a byte which will be written
// the wren_disp is the write enable for the ascii text ram. Only the wr_addr[8:0] are used as the character display is 32x16.
// the wren_font is the write enable for the font memort. Only 2 bits are use of the wr_dataa[1:0] and wr_addr[12:0] are used.
// tie these ports to GND for now disabling them
input clk, pc_ena, hde_in, vde_in, hs_in, vs_in ;
output osd_ena_out ;
reg osd_ena_out ;
output [2:0] osd_image ;
output hde_out, vde_out, hs_out, vs_out;
input wren_disp, wren_font;
input [15:0] wr_addr;
input [7:0] wr_data;
reg [9:0] disp_x,dly1_disp_x,dly2_disp_x;
reg [8:0] disp_y,dly1_disp_y,dly2_disp_y;
reg dena,dly1_dena,dly2_dena,dly3_dena,dly4_dena;
reg [7:0] dly1_letter, dly2_letter;
reg [7:0] hde_pipe, vde_pipe, hs_pipe, vs_pipe;
wire [12:0] font_pos;
wire [8:0] disp_pos;
wire [2:0] osd_image;
parameter PIPE_DELAY = 4; // This parameter selects the number of pixel clocks which the output VDE and syncs are delayed. Only use 2 through 9.
// ****************************************************************************************************************************
// LPM_RAM_DP Quartus dual port memory function
// ****************************************************************************************************************************
wire [7:0] sub_wire0;
wire [7:0] letter = sub_wire0[7:0];
// w access = 'b1100000a aaaaaaaa
altsyncram altsyncram_component_osd_mem ( .wren_a ( wren_disp ), .clock0 (clk), .clock1 (clk), .clocken1 (pc_ena),
.address_a (wr_addr[8:0]), .address_b (disp_pos[8:0]),
.data_a (wr_data[7:0]), .q_b (sub_wire0));
defparam
altsyncram_component_osd_mem.intended_device_family = "Cyclone",
altsyncram_component_osd_mem.operation_mode = "DUAL_PORT",
altsyncram_component_osd_mem.width_a = 8,
altsyncram_component_osd_mem.widthad_a = 9,
altsyncram_component_osd_mem.width_b = 8,
altsyncram_component_osd_mem.widthad_b = 9,
altsyncram_component_osd_mem.lpm_type = "altsyncram",
altsyncram_component_osd_mem.width_byteena_a = 1,
altsyncram_component_osd_mem.outdata_reg_b = "CLOCK1",
altsyncram_component_osd_mem.indata_aclr_a = "NONE",
altsyncram_component_osd_mem.wrcontrol_aclr_a = "NONE",
altsyncram_component_osd_mem.address_aclr_a = "NONE",
altsyncram_component_osd_mem.address_reg_b = "CLOCK1",
altsyncram_component_osd_mem.address_aclr_b = "NONE",
altsyncram_component_osd_mem.outdata_aclr_b = "NONE",
altsyncram_component_osd_mem.ram_block_type = "AUTO",
altsyncram_component_osd_mem.init_file = "osd_mem.mif";
// ****************************************************************************************************************************
wire [1:0] sub_wire1;
wire [1:0] osd_img = sub_wire1[1:0];
// w access = 'b111aaaaa aaaaaaaa
altsyncram altsyncram_component_osd_font ( .wren_a ( wren_font ), .clock0 (clk), .clock1 (clk), .clocken1 (pc_ena),
.address_a (wr_addr[12:0]), .address_b (font_pos[12:0]),
.data_a (wr_data[1:0]), .q_b (sub_wire1));
defparam
altsyncram_component_osd_font.intended_device_family = "Cyclone",
altsyncram_component_osd_font.operation_mode = "DUAL_PORT",
altsyncram_component_osd_font.width_a = 2,
altsyncram_component_osd_font.widthad_a = 13,
altsyncram_component_osd_font.width_b = 2,
altsyncram_component_osd_font.widthad_b = 13,
altsyncram_component_osd_font.lpm_type = "altsyncram",
altsyncram_component_osd_font.width_byteena_a = 1,
altsyncram_component_osd_font.outdata_reg_b = "CLOCK1",
altsyncram_component_osd_font.indata_aclr_a = "NONE",
altsyncram_component_osd_font.wrcontrol_aclr_a = "NONE",
altsyncram_component_osd_font.address_aclr_a = "NONE",
altsyncram_component_osd_font.address_reg_b = "CLOCK1",
altsyncram_component_osd_font.address_aclr_b = "NONE",
altsyncram_component_osd_font.outdata_aclr_b = "NONE",
altsyncram_component_osd_font.ram_block_type = "AUTO",
altsyncram_component_osd_font.init_file = "osd_font.mif";
// ****************************************************************************************************************************
// ****************************************************************************************************************************
// The disp_x is the X coordinate counter. It runs from 0 to 512 and stops there
// The disp_y is the Y coordinate sounter. It runs from 0 to 256 and stops there
assign disp_pos[4:0] = disp_x[8:4] ; // The disp_pos[4:0] is the lower address for the 32 characters for the ascii text.
assign disp_pos[8:5] = disp_y[7:4] ; // the disp_pos[8:5] is the upper address for the 16 lines of text
// The result from the ascii memory component 'altsyncram_component_osd_mem' is called letter[7:0]
// Since disp_pos[8:0] has entered the read address, it takes 2 pixel clock cycles for the resulting letter[7:0] to come out.
// Now, font_pos[12:0] is the read address for the memory block containing the font memory
assign font_pos[12:6] = letter[6:0] ; // Select the upper font address with the 7 bit letter, note the atari font has only 128 characters.
assign font_pos[2:0] = dly2_disp_x[3:1] ; // select the font X coordinate with a 2 pixel clock DELAYES disp_x address. [3:1] is used so that every 2 x pixels are repeats
assign font_pos[5:3] = dly2_disp_y[3:1] ; // select the font y coordinate with a 2 pixel clock DELAYES disp_y address. [3:1] is used so that every 2 y lines are repeats
// The resulting font image, 2 bits since I made a 2 bit color atari font is assigned to the OSD[1:0] output
// Also, since there is an 8th bit in the ascii text memory, I use that as a third OSD[2] output color bit.
assign osd_image[1:0] = osd_img[1:0];
assign osd_image[2] = dly2_letter[7]; // Remember, it takes 2 pixel clocks for osd_img[1:0] data to be valid from read address letter[6:0]
// so, if we want to use letter[7] as an upper color bit, is needs to be delayed 2 pixel clocks so it will be parallel with the osd_img[1:0] read data
always @ ( posedge clk ) begin
if (pc_ena) begin
// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
hde_pipe[0] <= hde_in;
hde_pipe[7:1] <= hde_pipe[6:0];
hde_out <= hde_pipe[PIPE_DELAY-2];
vde_pipe[0] <= vde_in;
vde_pipe[7:1] <= vde_pipe[6:0];
vde_out <= vde_pipe[PIPE_DELAY-2];
hs_pipe[0] <= hs_in;
hs_pipe[7:1] <= hs_pipe[6:0];
hs_out <= hs_pipe[PIPE_DELAY-2];
vs_pipe[0] <= vs_in;
vs_pipe[7:1] <= vs_pipe[6:0];
vs_out <= vs_pipe[PIPE_DELAY-2];
// **********************************************************************************************
// This OSD generator's window is only 512 pixels by 256 lines.
// Since the disp_X&Y counters are the screens X&Y coordinates,
// I'm using an extra most significant bit in the counters to determine if the
// OSD ena flag should be on or off
if (disp_x[9] || disp_y[8]) dena <= 0; // When disp_x > 511 or disp_y > 255, then turn off the OSD's output enable flag
else dena <= 1; // otherwise, turn on the OSD output enable flag.
if ( ~vde ) disp_y[8:0] <= 9'b111111111; // preset the disp_y counter to max while the vertical display is disabled
else if (hde_in && ~hde_pipe[0]) begin // isolate a single event at the begining of the active display area
disp_x[9:0] <= 10'b0000000000; // clear the disp_x counter
if (!disp_y[8] | (disp_y[8:7] == 2'b11)) disp_y <= disp_y + 1; // only increment the disp_y counter if it hasn't reached it's end
end else if (!disp_x[9]) disp_x <= disp_x + 1; // keep on addind to the disp_x counter until it reaches it's end.
// **********************************************************************************************
// *** These delay pipes registers are explaines in the 'assign's above.
// **********************************************************************************************
dly1_disp_x <= disp_x;
dly2_disp_x <= dly1_disp_x;
dly1_disp_y <= disp_y;
dly2_disp_y <= dly1_disp_y;
dly1_letter <= letter;
dly2_letter <= dly1_letter;
dly1_dena <= dena;
dly2_dena <= dly1_dena;
dly3_dena <= dly2_dena;
dly4_dena <= dly3_dena;
// **********************************************************************************************
osd_ena_out <= dly2_dena; // This is used to drive a graphics A/B switch which tells when the OSD graphics should be shown
// It needs to be delayes by the number of pixel clocks required for the above memories
end // ena
end // always@clk
endmodule
Before we make changes like addressing 80 columns, addressing anywhere withing a chunk of ram, I'll fetch out my palette and overlay generator which goes with this OSD generator. This will allow you to simultaneously run your pattern generator and superimpose the OSD generator on top, with a semi transparent layer in the font and a palette for the font colors. For now, you can just wire the 'OSD[2:0]' output's bits 0 to your r&g&b outputs.
Hint, in the block diagram editor, double click on a blank area and type 'WIRE'. Place these on your schematic and label on the left the buss name and number of my OSD outputs and on the right place a wire and label your RGB pins names.
Also, for the write memory inputs, on the block diagram editor, double click on a blank area, and type GND, and insert 3 GND symbols and tie them to the inputs on the OSD generator module.
The osd text memory block .mif should contain a display font test text of all the characters. You and edit the file in quartus to make it say anything you like.
Note: After the palette and we will tackle a true memory address generator which will allow text windows of any Z-size by any Y-size, pointing to any memory base address, with a hidden overscan or any text columns width. Plus, variable X&Y font size and 16 foreground by 16 background colors, paletted and programable for each letter on the display. We will also be embedding the font memory into the same memory as the ascii text ram which will also be you main 8bit cpu ram as well. There should be no problem implementing the 256 color graphics as well, but, the resolution window will be puny and we might need to forgo it's palette.
ALSO: I need to you increase your output pixel bits to 4 for each R,G,B, that's 12 bit color, or, 4096 colors. Do not erase your pattern generator, we will be still bringing it back soon for additional tests.