📄 vga_controller.pm
字号:
});
e_register->add({
out => {name => "fifo_has_data", export => 0,},
in => "fifo_has_data_reg1",
clock => "vga_clk",
enable => 1,
});
################################################################
# THIS IS THE DMA ADDRESS COUNTER #
################################################################
e_blind_instance->add({
tag => 'normal',
use_sim_models => 1,
name => 'dma_address_counter',
module => 'lpm_counter',
in_port_map => {clock => "clk",
aclr => "!reset_n",
cnt_en => "address_counter_incr",
sload => "address_counter_sload",
data => "dma_source_reg[31:2]",},
out_port_map => {"q", "address_counter_temp"},
parameter_map => {LPM_WIDTH => "30"},
});
e_signal->add({name => "address_counter_temp", width => 30,});
e_signal->add({name => "address_counter", width => 32,});
e_assign->add (["address_counter_incr", "(master_read == 1) && (master_waitrequest == 0) && (go_bit == 1)"]);
e_assign->add (["address_counter_sload", "(go_bit == 0) ||(address_counter_incr && (address_counter == last_dma_addr_reg))"]);
e_assign->add (["address_counter", "{address_counter_temp, 2'b00}"]);
# Register the DMA end address too, since the sw driver can change the
# dma_source_reg register on which it depends during a buffer flip.
e_register->add({
out => {name => "last_dma_addr_reg", width => $SBI_slave->{Data_Width}, export => 0,},
in => "(dma_source_reg + dma_modulus_reg - 4)",
enable => "address_counter_sload",
});
################################################################
# THIS IS THE VGA COLUMN COUNTER #
################################################################
my $column_counter_width = ceil(log2($HSCAN_WIDTH));
e_blind_instance->add({
tag => 'normal',
use_sim_models => 1,
name => 'vga_column_counter',
module => 'lpm_counter',
in_port_map => {clock => "vga_clk",
aclr => "!reset_n",
cnt_en => "vga_start",
sclr => "!vga_start"},
out_port_map => {"q", "column_counter",},
parameter_map => {LPM_WIDTH => "$column_counter_width",
LPM_MODULUS => "$HSCAN_WIDTH"},
});
e_signal->add({name => "column_counter", width => "$column_counter_width",});
# Temporary for debug
#e_port->add({name => "column_counter", width => 10, direction => "output", });
################################################################
# THIS IS THE VGA ROW COUNTER #
################################################################
my $row_counter_width = ceil(log2($VSCAN_DEPTH));
e_blind_instance->add({
tag => 'normal',
use_sim_models => 1,
name => 'vga_row_counter',
module => 'lpm_counter',
in_port_map => {clock => "vga_clk",
aclr => "!reset_n",
cnt_en => "vga_start && (column_counter == ($HSCAN_WIDTH - 1))",
sclr => "!vga_start",},
out_port_map => {"q", "row_counter",},
parameter_map => {LPM_WIDTH => "$row_counter_width",
LPM_MODULUS => "$VSCAN_DEPTH"},
});
e_signal->add({name => "row_counter", width => "$row_counter_width",});
# Temporary for debug
#e_port->add({name => "row_counter", width => 10, direction => "output", });
################################################################
# THIS IS THE VGA INIT LOGIC #
################################################################
# We need to wait until everything's set up before we release
# the fury of the go bit. Also, if the fifo loses sync during normal
# operation, we drive the go_bit low again.
e_register->add({
out => {name => "go_bit", export => 0,},
in => "ctrl_reg_go_bit & stop_config_counter & fifo_emptied",
enable => 1,
});
# This is a config counter that we decode below for the VGA init sequence
e_signal->add({name => "config_counter", width => 3,});
e_assign->add (["stop_config_counter", "config_counter == 3'b101"]);
e_blind_instance->add({
tag => 'normal',
use_sim_models => 1,
name => 'vga_config_counter',
module => 'lpm_counter',
in_port_map => {clock => "vga_clk",
aclr => "!reset_n",
cnt_en => "!stop_config_counter",},
out_port_map => {"q", "config_counter"},
parameter_map => {LPM_WIDTH => "3"},
});
# This gives us a config sequence for the VGA DAC. The sequence is as follows:
# sync_n -> 0-1-0-0-1-1
# sync_t -> 0-0-0-0-0-0
# M1 -> 0-0-0-0-0-0
# M2 -> 0-0-1-1-0-0
e_register->add({
out => {name => "sync_n_init", export => 0,},
in => "(config_counter[2] | (config_counter[0] & !config_counter[1]))",
clock => "vga_clk",
enable => 1,
});
e_register->add({
out => {name => "M2", export => 1,},
in => "config_counter[1]",
clock => "vga_clk",
enable => 1,
});
e_assign->add (["M1", 0]);
# This holds the fifo read_req signal high until the thing's empty. Used for bring-up
# and disaster recovery.
e_register->add({
out => {name => "fifo_emptied", export => 0,},
in => "(ctrl_reg_go_bit & (fifo_emptied | fifo_rdempty))",
clock => "vga_clk",
enable => 1,
});
e_assign->add (["empty_the_fifo", "!fifo_emptied"]);
################################################################
# THIS IS THE VGA CONTROL LOGIC #
################################################################
# This is a version of the go bit in the control register, but here, it's
# double-registered on the 25MHz clock to sync it up. We'll use it in our
# logic that determines when we're all set to start the VGA sequencer
e_register->add({
out => {name => "go_bit_vga_reg1", export => 0,},
in => "go_bit",
clock => "vga_clk",
enable => 1,
});
e_register->add({
out => {name => "go_bit_vga", export => 0,},
in => "go_bit_vga_reg1",
clock => "vga_clk",
enable => 1,
});
# This signals when we're okay to start the VGA sequencer
e_register->add({
out => {name => "vga_start", export => 0,},
in => "(vga_start & go_bit_vga) | (fifo_has_data & go_bit_vga)",
clock => "vga_clk",
enable => 1,
});
# If the color depth is 16-bits, we just use a mux to pull 16 bits at a
#time out of the fifo's 32 bit output
if($WSA->{color_depth} eq "16")
{
# This is the data splitter mux. We get data out of the fifo as 32-bit
# words. Only we need 16-bit words. So we time-div-mux it with a toggling flip flop
# and only do a real read of the fifo every other cycle.
e_signal->add({name => "vga_16bit_out", width => 16,});
e_assign->add (["vga_16bit_out", "mux_toggle ? fifo_data_out[31:16] : fifo_data_out[15:0]"]);
# And the mux toggler flip flop
e_register->add({
out => {name => "mux_toggle", export => 0,},
in => "(!mux_toggle) & (read_16b)",
clock => "vga_clk",
enable => 1,
async_value => 0,
});
# The read request that actually goes to the fifo is essentially the output
# of the mux toggler. We also or-in a read signal that empties the fifo during
# the init sequence.
e_assign->add (["fifo_read_req", "((mux_toggle & read_16b) | empty_the_fifo)"]);
# Here, we issue a 16-bit read whenever we're not blanking the VGA.
# We already have logic that holds off the VGA sequencer until we
# know there is data in the fifo. And since hblank and vblank are
# created by the sequencer, they wont go high until the fifo's got
# data to read
e_assign->add (["read_16b", "hblank & vblank"]);
# The read signal is also our active signal, so we'll give it a
# descriptive name.
e_assign->add (["display_active", "read_16b"]);
# This splits up the 16-bit data into RGB.
e_port->add({name => "R", width => 8, direction => "output", });
e_register->add({
out => {name => "R", export => 1,},
in => "display_active ? ({vga_16bit_out[15:11], 3'b111}) : 8'b00000000",
clock => "vga_clk",
enable => 1,
});
e_port->add({name => "G", width => 8, direction => "output", });
e_register->add({
out => {name => "G", export => 1,},
in => "display_active ? ({vga_16bit_out[10:5], 2'b11}) : 8'b00000000",
clock => "vga_clk",
enable => 1,
});
e_port->add({name => "B", width => 8, direction => "output", });
e_register->add({
out => {name => "B", export => 1,},
in => "display_active ? ({vga_16bit_out[4:0], 3'b111}) : 8'b00000000",
clock => "vga_clk",
enable => 1,
});
}
# If the color depth is 24 bits packed, our muxing is a little more complicated.
# Every set of 3 32-bit words contains 4 24-bit pixels
if($WSA->{color_depth} eq "24")
{
# This stores the fifo output for one extra cycle because sometimes pieces of
# our 24-bit pixels need to be picked out of the previous 32-bit word.
e_register->add({
out => {name => "prev_fifo_data_out", width => "32", export => 0,},
in => "fifo_data_out",
clock => "vga_clk",
enable => "fifo_read_req",
});
# This is the mux counter. Every time we pull out 24-bits, our byte offset changes.
# This counter is here to keep track of what that byte-offset is so we always get
# the proper 24-bits
e_signal->add({name => "pixel_mux_counter", width => 2,});
e_blind_instance->add({
tag => 'normal',
use_sim_models => 1,
name => 'vga_pixel_mux_counter',
module => 'lpm_counter',
in_port_map => {clock => "vga_clk",
aclr => "!reset_n",
cnt_en => "read_24b",
sclr => "!go_bit_vga",},
out_port_map => {"q", "pixel_mux_counter"},
parameter_map => {LPM_WIDTH => "2"},
});
# Here, we issue a 24-bit read whenever we're not blanking the VGA.
# We already have logic that holds off the VGA sequencer until we
# know there is data in the fifo. And since hblank and vblank are
# created by the sequencer, they wont go high until the fifo's got
# data to read
e_assign->add (["read_24b", "hblank & vblank"]);
# The read signal is also our active signal, so we'll give it a
# descriptive name.
e_assign->add (["display_active", "read_24b"]);
# The fifo skips a read once every 3 words (4 pixels)
e_assign->add (["fifo_read_req", "(((pixel_mux_counter != 3) & read_24b) | empty_the_fifo)"]);
# This is the data splitter mux. We get data out of the fifo as 32-bit
# words. Only we need 24-bit pixels. So we do a fancy little mux dance.
e_signal->add({name => "vga_24bit_out", width => 24,});
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -