-------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 11:25:45 10/15/05 -- Design Name: -- Module Name: char_gen - Behavioral -- Project Name: -- Target Device: -- Tool versions: -- Description: -- -- Dependencies: -- -- Revision: -- Revision 0.01 - File Created -- Additional Comments: -- -------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use work.utils_pkg.all; use work.vga_types.all; entity char_gen is Generic ( tsvga : vga_timespec_t := ts_vga_800_600_72; -- User supplied parameters CHARACTER_SCALE : natural := 1; CHAR_ROM_SIZE_X : natural := 8; -- Pixels CHAR_ROM_SIZE_Y : natural := 16; -- Pixels CHAR_ROM_DEPTH : natural := 256; -- Chars MAX_CHAR_PER_LINE : natural := 32; -- Chars MAX_LINE_PER_FRAME : natural := 16; -- Chars CHAR_SPACING_X : natural := 0; -- Pixels CHAR_SPACING_Y : natural := 0; -- Pixels SCREEN_OFFSET_X : natural := 16; -- Pixels SCREEN_OFFSET_Y : natural := 16 -- Pixels ); Port ( -- System signals rst : in std_logic; vga_clk : in std_logic; sys_clk : in std_logic; ce : in std_logic; frame_pulse : in std_logic; -- Host Signals clr_screen : in std_logic; clr_line : in std_logic; din : in unsigned(7 downto 0); dout : out unsigned(7 downto 0); ascii_we : in std_logic; posy_we : in std_logic; posx_we : in std_logic; ready : out std_logic; -- Control signals scan_pos_x_in : in natural range 0 to tsvga.ts_h.ncyc_scan-1; scan_pos_y_in : in natural range 0 to tsvga.ts_v.ncyc_scan-1; -- Status signals csr_pos_x_out : out natural range 0 to MAX_CHAR_PER_LINE-1; csr_pos_y_out : out natural range 0 to MAX_LINE_PER_FRAME-1; -- Output char_draw_en : out std_logic ); end char_gen; architecture Behavioral of char_gen is constant A_RAM_ADDR_WIDTH : integer := 12; constant A_RAM_DATA_WIDTH : integer := 8; type offset_rom_t is array (0 to MAX_LINE_PER_FRAME-1) of unsigned(A_RAM_ADDR_WIDTH-1 downto 0); function gen_offset_rom(num_char_per_line, num_lines : natural) return offset_rom_t is variable result : offset_rom_t; variable offset : unsigned(A_RAM_ADDR_WIDTH-1 downto 0) := (others => '0'); begin for i in 0 to num_lines-1 loop result(i) := offset; offset := offset + to_unsigned(num_char_per_line, A_RAM_ADDR_WIDTH-1); end loop; return result; end gen_offset_rom; constant offset_rom : offset_rom_t := gen_offset_rom(MAX_CHAR_PER_LINE, MAX_LINE_PER_FRAME); -- Calculated parameters type cg_state_t is (cg_init, cg_ready, cg_clr_line, cg_clr_screen); signal s, sn : cg_state_t; constant CELL_SIZE_X : natural := (CHAR_ROM_SIZE_X+CHAR_SPACING_X)*CHARACTER_SCALE; constant CELL_SIZE_Y : natural := (CHAR_ROM_SIZE_Y+CHAR_SPACING_Y)*CHARACTER_SCALE; constant CHAR_SIZE_X : natural := CHAR_ROM_SIZE_X*CHARACTER_SCALE; constant CHAR_SIZE_Y : natural := CHAR_ROM_SIZE_Y*CHARACTER_SCALE; signal line_offset : natural range 0 to MAX_LINE_PER_FRAME-1; signal cell_x_cnt : natural range 0 to MAX_CHAR_PER_LINE-1; signal cell_y_cnt : natural range 0 to MAX_LINE_PER_FRAME-1; signal char_x_cnt : natural range 0 to CELL_SIZE_X-1; signal char_y_cnt : natural range 0 to CELL_SIZE_Y-1; signal char_rom_row : unsigned(NextExpBaseTwo(CHAR_SIZE_Y)-1 downto 0); signal char_rom_addr : unsigned(NextExpBaseTwo(CHAR_ROM_DEPTH*CHAR_ROM_SIZE_Y)-1 downto 0); signal char_rom_data : unsigned(CHAR_ROM_SIZE_X-1 downto 0); signal char_reg : unsigned((CHARACTER_SCALE*CHAR_ROM_SIZE_X)-1 downto 0); signal a_addr_rd : unsigned(A_RAM_ADDR_WIDTH-1 downto 0); signal a_addr_wr : unsigned(A_RAM_ADDR_WIDTH-1 downto 0); signal a_data_rd : unsigned(A_RAM_DATA_WIDTH-1 downto 0); signal a_data_wr : unsigned(A_RAM_DATA_WIDTH-1 downto 0); signal a_ram_we : std_logic; signal a_cnt : natural range 0 to 2**A_RAM_ADDR_WIDTH-1; signal a_cnt_rst : std_logic; signal a_cnt_en : std_logic; signal csr_posx_reg : natural range 0 to MAX_CHAR_PER_LINE-1; signal csr_posy_reg : natural range 0 to MAX_LINE_PER_FRAME-1; signal a_data_reg : unsigned(A_RAM_DATA_WIDTH-1 downto 0); signal a_data_we : std_logic; signal csr_posx_overflow : std_logic; signal csr_posx_cnt_din : natural range 0 to MAX_CHAR_PER_LINE-1; signal csr_posx_cnt_set : std_logic; signal csr_posx_cnt_en : std_logic; signal csr_posy_overflow : std_logic; signal csr_posy_cnt_din : natural range 0 to MAX_LINE_PER_FRAME-1; signal csr_posy_cnt_set : std_logic; signal csr_posy_cnt_en : std_logic; signal line_addr : unsigned(A_RAM_ADDR_WIDTH-1 downto 0); signal char_addr : unsigned(A_RAM_ADDR_WIDTH-1 downto 0); -- Visible Cursor constant CURSOR_BLINK_INTERVAL : natural := tsvga.nfps/2; type cursor_rom_t is array (0 to 2*CHAR_ROM_SIZE_Y-1) of unsigned (CHAR_ROM_SIZE_X-1 downto 0); constant cursor_rom : cursor_rom_t := ( -- Cursor off "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "00000000", "11111111", "11111111", "11111111", -- Cursor on "00000000", "00000000", "11111111", "11111111", "11111111", "11111111", "11111111", "11111111", "11111111", "11111111", "11111111", "11111111", "11111111", "00000000", "00000000", "00000000" ); signal cursor_rom_addr : unsigned(NextExpBaseTwo(2*CHAR_ROM_SIZE_Y)-1 downto 0); signal cursor_rom_data : unsigned(CHAR_ROM_SIZE_X-1 downto 0); signal cursor_visible : std_logic; signal cursor_cnt : natural range 0 to CURSOR_BLINK_INTERVAL-1; signal csr_posx_synced : natural range 0 to MAX_CHAR_PER_LINE-1; signal csr_posy_synced : natural range 0 to MAX_LINE_PER_FRAME-1; -- Scrolling signal scroll_offset : natural range 0 to MAX_LINE_PER_FRAME-1; signal scroll_active : std_logic; begin a_addr_rd <= to_unsigned(cell_x_cnt, a_addr_rd'length) + offset_rom(line_offset); line_addr <= offset_rom(csr_posy_reg); char_addr <= to_unsigned(csr_posx_reg, char_addr'length); char_rom_addr <= a_data_rd & char_rom_row(NextExpBaseTwo(CHAR_SIZE_Y)-1 downto (CHARACTER_SCALE-1)); cursor_rom_addr <= cursor_visible & char_rom_row(NextExpBaseTwo(CHAR_SIZE_Y)-1 downto (CHARACTER_SCALE-1)); csr_pos_x_out <= csr_posx_reg; csr_pos_y_out <= csr_posy_reg; inst_char_rom : entity work.char_rom PORT MAP ( CLK => vga_clk, ADDR => char_rom_addr, DATA => char_rom_data ); inst_ascii_ram: entity work.dpram_1w1r2c_ro GENERIC MAP ( addr_width => A_RAM_ADDR_WIDTH, data_width => A_RAM_DATA_WIDTH ) PORT MAP( clk_a => sys_clk, clk_b => vga_clk, we_a => a_ram_we, re_b => '1', addr_a => a_addr_wr, addr_b => a_addr_rd, din_a => a_data_wr, dout_b => a_data_rd ); --------------------------------------------------------------------------------------------- --- ASCII RAM control --------------------------------------------------------------------------------------------- asci_ram_fsm: process (s, a_cnt, clr_line, clr_screen, a_data_we, a_data_reg, char_addr, line_addr) begin sn <= s; a_cnt_rst <= '0'; a_cnt_en <= '0'; a_ram_we <= '0'; a_data_wr <= a_data_reg; a_addr_wr <= char_addr + line_addr; ready <= '0'; case s is when cg_init => sn <= cg_clr_screen; a_cnt_rst <= '1'; when cg_ready => ready <= '1'; a_ram_we <= a_data_we; if clr_screen = '1' then a_cnt_rst <= '1'; sn <= cg_clr_screen; elsif clr_line = '1' then a_cnt_rst <= '1'; sn <= cg_clr_line; end if; when cg_clr_line => a_ram_we <= '1'; a_cnt_en <= '1'; a_data_wr <= to_unsigned(0, A_RAM_DATA_WIDTH); a_addr_wr <= to_unsigned(a_cnt + to_integer(line_addr), A_RAM_ADDR_WIDTH); if a_cnt = MAX_CHAR_PER_LINE-1 then sn <= cg_ready; end if; when cg_clr_screen => a_ram_we <= '1'; a_cnt_en <= '1'; a_data_wr <= to_unsigned(0, A_RAM_DATA_WIDTH); a_addr_wr <= to_unsigned(a_cnt, A_RAM_ADDR_WIDTH); if a_cnt = (2**A_RAM_ADDR_WIDTH-1) then sn <= cg_ready; end if; when others => sn <= cg_ready; end case; end process; asci_ram_fsm_next: process (sys_clk) begin if rising_edge(sys_clk) then if rst = '1' then s <= cg_init; else s <= sn; end if; end if; end process; --------------------------------------------------------------------------------------------- asci_ram_cnt: process (sys_clk) begin if rising_edge(sys_clk) then if a_cnt_rst = '1' then a_cnt <= 0; elsif a_cnt_en = '1' then if a_cnt < (2**A_RAM_ADDR_WIDTH-1) then a_cnt <= a_cnt + 1; end if; end if; end if; end process; --------------------------------------------------------------------------------------------- asci_addr_reg: process (sys_clk) begin if rising_edge(sys_clk) then a_data_we <= '0'; csr_posx_cnt_en <= '0'; csr_posx_cnt_set <= '0'; csr_posy_cnt_en <= '0'; csr_posy_cnt_set <= '0'; if rst = '1' then a_data_reg <= (others => '0'); elsif posy_we = '1' then csr_posy_cnt_din <= to_integer(din); csr_posy_cnt_set <= '1'; elsif posx_we = '1' then csr_posx_cnt_din <= to_integer(din); csr_posx_cnt_set <= '1'; elsif ascii_we = '1' then dout <= din; -- written char is simply echoed back to dout if din < X"20" then if din = X"0D" then csr_posx_cnt_din <= 0; csr_posx_cnt_set <= '1'; elsif din = X"0A" then csr_posy_cnt_en <= '1'; end if; else a_data_reg <= din - X"20"; a_data_we <= '1'; csr_posx_cnt_en <= '1'; end if; end if; end if; end process; asci_csr_posx_counter: process (sys_clk) begin if rising_edge(sys_clk) then csr_posx_overflow <= '0'; if rst = '1' then csr_posx_reg <= 0; elsif csr_posx_cnt_set = '1' then csr_posx_reg <= csr_posx_cnt_din; elsif csr_posx_cnt_en = '1' then if csr_posx_reg < MAX_CHAR_PER_LINE-1 then csr_posx_reg <= csr_posx_reg + 1; else csr_posx_reg <= 0; csr_posx_overflow <= '1'; end if; end if; end if; end process; asci_csr_posy_counter: process (sys_clk) begin if rising_edge(sys_clk) then csr_posy_overflow <= '0'; if rst = '1' then csr_posy_reg <= 0; elsif csr_posy_cnt_set = '1' then csr_posy_reg <= csr_posy_cnt_din; elsif csr_posy_cnt_en = '1' or csr_posx_overflow = '1' then if csr_posy_reg < MAX_LINE_PER_FRAME-1 then csr_posy_reg <= csr_posy_reg + 1; else csr_posy_reg <= 0; csr_posy_overflow <= '1'; end if; end if; end if; end process; scroll_gen: process (sys_clk) begin if rising_edge(sys_clk) then if rst = '1' or clr_screen = '1' then scroll_offset <= 0; scroll_active <= '0'; elsif csr_posy_cnt_set = '1' then scroll_active <= '0'; elsif csr_posy_overflow = '1' then scroll_active <= '1'; scroll_offset <= 1; end if; if (csr_posy_cnt_en = '1' or csr_posx_overflow = '1') and scroll_active = '1' then scroll_offset <= scroll_offset + 1; end if; end if; end process; --------------------------------------------------------------------------------------------- --- Cursor generator --------------------------------------------------------------------------------------------- proc_sync_csr_posx: process(vga_clk) variable p1, p2 : natural range 0 to MAX_CHAR_PER_LINE-1; begin if rising_edge(vga_clk) then csr_posx_synced <= p2; p2 := p1; p1 := csr_posx_reg; end if; end process; proc_sync_csr_posy: process(vga_clk) variable p1, p2 : natural range 0 to MAX_LINE_PER_FRAME-1; begin if rising_edge(vga_clk) then csr_posy_synced <= p2; p2 := p1; p1 := csr_posy_reg; end if; end process; csr_blink_cnt: process (rst, vga_clk) begin if rst = '1' then cursor_cnt <= 0; cursor_visible <= '0'; elsif rising_edge(vga_clk) then if frame_pulse = '1' then if cursor_cnt < CURSOR_BLINK_INTERVAL-1 then cursor_cnt <= cursor_cnt + 1; else cursor_cnt <= 0; cursor_visible <= not cursor_visible; end if; end if; end if; end process; cursor_rom_read: process (vga_clk) begin if rising_edge(vga_clk) then cursor_rom_data <= (others => '0'); if (cell_x_cnt = csr_posx_synced) and (line_offset = csr_posy_synced) then cursor_rom_data <= cursor_rom(to_integer(cursor_rom_addr)); end if; end if; end process; --------------------------------------------------------------------------------------------- --- Character generator --------------------------------------------------------------------------------------------- char_rom_ctrl: process (vga_clk) begin if rising_edge(vga_clk) then char_rom_row <= to_unsigned(char_y_cnt, char_rom_row'length); end if; end process; --------------------------------------------------------------------------------------------- char_reg_ctrl: process (vga_clk) begin if rising_edge(vga_clk) and ce = '1' then if char_x_cnt = 2 then -- +2 delay for ASCII-RAM access and CHAR-ROM access for i in 0 to CHAR_ROM_SIZE_X-1 loop for j in 0 to CHARACTER_SCALE-1 loop char_reg(i*CHARACTER_SCALE + j) <= char_rom_data(i) xor cursor_rom_data(i); end loop; end loop; else char_reg <= char_reg(char_reg'left-1 downto 0) & '0'; end if; end if; end process; --------------------------------------------------------------------------------------------- cg_ctrl: process (rst, vga_clk) variable enable_x : std_logic; variable enable_y : std_logic; variable char_en : std_logic; variable char_en2 : std_logic; begin if (rst = '1') then enable_x := '0'; enable_y := '0'; line_offset <= 0; cell_x_cnt <= 0; cell_y_cnt <= 0; char_x_cnt <= 0; char_y_cnt <= 0; char_en := '0'; elsif rising_edge(vga_clk) and ce = '1' then char_draw_en <= char_en2 and char_reg(char_reg'left); char_en2 := char_en; char_en := '0'; if scan_pos_y_in = SCREEN_OFFSET_Y then enable_y := '1'; cell_y_cnt <= 0; char_x_cnt <= 0; line_offset <= scroll_offset; end if; if scan_pos_x_in = SCREEN_OFFSET_X and enable_y = '1' then enable_x := '1'; cell_x_cnt <= 0; char_x_cnt <= 0; elsif enable_x = '1' and enable_y = '1' then if char_x_cnt < CELL_SIZE_X-1 then char_x_cnt <= char_x_cnt + 1; else char_x_cnt <= 0; if cell_x_cnt < MAX_CHAR_PER_LINE-1 then cell_x_cnt <= cell_x_cnt + 1; else enable_x := '0'; if char_y_cnt < CELL_SIZE_Y-1 then char_y_cnt <= char_y_cnt + 1; else char_y_cnt <= 0; if cell_y_cnt < MAX_LINE_PER_FRAME-1 then cell_y_cnt <= cell_y_cnt + 1; else enable_y := '0'; end if; if line_offset < MAX_LINE_PER_FRAME-1 then line_offset <= line_offset + 1; else line_offset <= 0; end if; end if; end if; end if; if (char_x_cnt < CHAR_SIZE_X) and (char_y_cnt < CHAR_SIZE_Y) then char_en := '1'; end if; end if; end if; end process; --------------------------------------------------------------------------------------------- end Behavioral;