📄 em_dma.pm
字号:
use europa_all;use europa_utils;use e_avalon_master;use e_fsm;use strict;my $p4_revision = '$Revision: #1 $';my $p4_datetime = '$DateTime: 2005/08/05 18:28:42 $';$p4_revision =~ /#(\d+)/;my $revision = $1;my $read_master_name = "read_master";my $write_master_name = "write_master";my $control_port_name = "control_port_slave";my @all_transactions = qw( quadword doubleword word hw byte);sub control_register{ my ($comment, $name, $width, $condition, $data, $else_if, $else_event, $reset_value) = @_; # If there's nothing to do, return an empty list. return () if ($width == 0); # Fix up the reset value with the proper width. $reset_value =~ s/^[0-9]*\'h/$width\'h/; # Make a table for my mux... my @table = ($condition, $data); if ($else_if) { push @table, ($else_if, $name . $else_event); } # ... a mux for my register input... my $mux = e_mux->new({ lhs => e_signal->new(["p1_$name",]), table => \@table, type => "priority", default => $name, }); # ... and a register. my $reg = e_register->new({ comment => $comment, in => "p1_$name", out => e_signal->new({name => $name, width => $width}), async_value => $reset_value, }); return ($reg, $mux);}sub get_slave_port_data_width{ # Find the max width value over all register descriptors. return max(map {$_->[2]} get_slave_port_registers(@_));}sub get_slave_port_addr_width{ my @registers = get_slave_port_registers(@_); my $addr_width = ceil(log2(0 + @registers)); return $addr_width;}sub get_max_transaction_size_in_bits{ my @trans = reverse get_transaction_size_bit_names(); my $size = 8; @trans = map { my $this_size = $size; $size *= 2; {trans => $_, size => $this_size} } @trans; my $max_size = -1; map {$max_size = $_->{size} if is_transaction_allowed($_->{trans})} @trans; ribbit("no possible transactions!") if $max_size == -1; return $max_size;}# Transaction bit names, in a list with the least-significant bit# (the one encoding "byte" transaction") in the last position.sub get_transaction_size_bit_names{ return @all_transactions;}# We're interested in the indices of the control bits that encode the# transaction size in a few places, so here's a handy function.# # Bit indices are turned in a list with the least-significant bit# (the one encoding "byte" transaction") in the last position.sub get_transaction_size_bit_indices{ return get_slave_port_bits('control', get_transaction_size_bit_names());}# get_transaction_size_expression()# Get the list of control bit indices, least-significant ("byte") last;# form the expression of the associated control bit in the control register;# return the concatenation of all such control bits.# # The returned expression is appropriate for use as a number, namely a# read- or write-master address increment or length register decrement.# # Optimization: if the user promised to use any transaction sizes, exclude# their control bits.sub get_transaction_size_expression{ return concatenate( map {is_transaction_allowed($_) ? $_ : "1'b0"} get_transaction_size_bit_names() );}sub get_slave_port_registers{ my $Options = shift; # Historical note: registers "reserved1" and "reserved2" below # used to be read and write increment override. The list contents for # those register positions was formerly:# [" read master increment override",# "readincov",# $Options->{readincovwidth},# "",# "dma_ctl_writedata",# "",# "",# 0,# ],# [" write master increment override",# "writeincov",# $Options->{writeincovwidth},# "",# "dma_ctl_writedata",# "",# "",# 0,# ],# Likewise, "reserved3" used to be "altcontrol". Documenting that# register seems difficult, and it's likely of limited utility to# most people, so it's now renamed.# [" control register alternate",# "alt_control",# $control_register_width,# "",# "dma_ctl_writedata",# "",# "",# $control_default_reset_string,# ], # This function can be called at generate time in which case # $Options is a hash of various useful things from the ptf file, # or it can be called by generate_appurtenances, in which case # $Options is undef. # Here are the registers accessible from the slave port. # Some of these widths depend on user choices/slave sizes, etc. # But the status and control register widths are known. Go inspect # the bit defs to figure those widths out. # [register_name, bit_name, bit_pos, comment] # my @bits = get_slave_port_bit_definitions(); my @controlbits = get_control_bits(); my @statusbits = get_status_bits(); my $control_register_width = 1 + max(map {$_->[2]} @controlbits); my $status_register_width = 1 + max(map {$_->[2]} @statusbits); my $transaction_size = get_transaction_size_expression(); # Go figure out a decent default reset value for the control register. # I declare that the 'word' and 'leen' bits should be set. my $control_default_reset_string = 0; map { $control_default_reset_string += (($_->[1] =~ /(^word$)|(leen)/) ? 1 << $_->[2] : 0) } get_control_bits(); # Don't change the format of this table without first looking at all its # dependencies! my @reg_info = ( [" status register", "status", $status_register_width, "", "0", "", "", 0, ], [" read address", "readaddress", $Options->{readaddresswidth}, "", "dma_ctl_writedata", "inc_read", " + readaddress_inc", 0, ], [" write address", "writeaddress", $Options->{writeaddresswidth}, "", "dma_ctl_writedata", "inc_write", " + writeaddress_inc", 0, ], [" length in bytes", "length", # width should be no greater than the max address width, but at least # as large as lengthwidth. min( $::g_max_address_width, max( $Options->{lengthwidth}, Bits_To_Encode($Options->{max_slave_address_span}), ), ), "", "dma_ctl_writedata", "inc_read && (!length_eq_0)", $Options->{burst_enable} ? " - length" : " - $transaction_size", 0, ], [" reserved", "reserved1", 0, "", "dma_ctl_writedata", "", "", 0, ], [" reserved", "reserved2", 0, "", "dma_ctl_writedata", "", "", 0, ], [" control register", "control", $control_register_width, "", "dma_ctl_writedata", "", "", $control_default_reset_string, ], [" control register alternate", "reserved3", $control_register_width, "", "dma_ctl_writedata", "", "", $control_default_reset_string, ], ); # If there are any over-ride reset values in Options, set them now. # Meanwhile, fill in the write-condition expression, using the array # position as register address. my $index = 0; my $control_reg_index = -1; my $alt_control_reg_index = -1; for my $reg_spec (@reg_info) { # Most registers have the following default write select. $reg_spec->[3] = "dma_ctl_chipselect & ~dma_ctl_write_n & (dma_ctl_address == $index)"; $control_reg_index = $index if ($reg_spec->[1] eq "control"); $alt_control_reg_index = $index if ($reg_spec->[1] eq "reserved3"); $index++; my $name = $reg_spec->[1]; my $reset_value = $Options->{$name . "_reset_value"}; if (defined($reset_value)) { # Reset values can be decimal or hex (0x...). Perl's handy # 'oct' function handles hex (starts with 0x) and octal (starts with 0). $reset_value = eval($reset_value); # $reset_value = oct($reset_value) if $reset_value =~ /^0/; # The width is set to the max here; it'll be fixed up later. $reg_spec->[7] = sprintf("%d'h%X", $::g_max_register_width, $reset_value); } } ribbit ("can't find control register\n") if (-1 == $control_reg_index); ribbit ("can't find alt_control register\n") if (-1 == $alt_control_reg_index); # Since I've decided to make an alternate control register, aliased # to the control register, let writes to it go to the real control. $reg_info[$control_reg_index]->[3] = "dma_ctl_chipselect & ~dma_ctl_write_n & " . "((dma_ctl_address == $control_reg_index) || " . "(dma_ctl_address == $alt_control_reg_index))"; # Say, wouldn't it be handy to be able to specify reset values for # control register bits by name? If any such are found, they override # the control register reset value. my $cleared_bits = ~0; # That is, all 1-bits. my $set_bits = 0; # Bleh. Go grab the current reset value, and convert it back to a decimal # number if necessary. my $cur_reset = $reg_info[$control_reg_index]->[7]; $cur_reset =~ s/[0-9]*\'h/0x/g; $cur_reset = eval($cur_reset); my $i = 0; for (get_control_bits()) { my $bitname = $_->[1]; my $optionname = "control_" . $bitname . "_reset_value"; my $override_bit = $Options->{"control_" . $bitname . "_reset_value"}; if (defined($override_bit)) { if ($override_bit) { $set_bits |= 1 << $i; } else { $cleared_bits &= ~(1 << $i); } } $i++; } $cur_reset |= $set_bits; $cur_reset &= $cleared_bits; # A few sanity checks: I'm not going to enforce these at run-time, since # that would cost logic, but I might as well prevent silly errors in the # ptf-file reset values. Why allow my gun to point directly at my foot? # Only one of byte, hw, word # should be true. my @check_bits = get_transaction_size_bit_indices(); if (1 != grep {$cur_reset & (1 << $_)} @check_bits) { ribbit( sprintf( "Multiple bits set in bogus control register reset value 0x%X\n", $cur_reset) ); } # The width is set to the max here; it'll be fixed up later. $reg_info[$control_reg_index]->[7] = sprintf("%d'h%X", $::g_max_register_width, $cur_reset); return @reg_info;}sub get_control_bits{ my @control_bits = ( ["byte", "Byte transaction", ], ["hw", "Half-word transaction", ], ["word", "Word transaction", ], ["go", "enable execution", ], ["i_en", "enable interrupt", ], ["reen", "Enable read end-of-packet", ], ["ween", "Enable write end-of-packet", ], ["leen", "Enable length=0 transaction end", ], ["rcon", "Read from a fixed address", ], ["wcon", "Write to a fixed address", ], ["doubleword", "Double-word transaction", ], ["quadword", "Quad-word transaction", ], ["softwarereset", "Software reset - write twice in succession to reset", ], ); # Convert the above simple list into a list of listrefs of the form: # [register name, bit name, bit position, comment]. my $i = 0; return map {["control", $_->[0], $i++, $_->[1]]} @control_bits;}sub get_status_bits{ my @status_bits = ( [ "done", "1 when done. Status write clears.", ], [ "busy", "1 when busy.", ], [ "reop", "read-eop received",], [ "weop", "write-eop received",], [ "len", "requested length transacted",], ); # Convert the above simple list into a list of listrefs of the form: # [register name, bit name, bit position, comment]. my $i = 0; return map {["status", $_->[0], $i++, $_->[1]]} @status_bits;}sub get_slave_port_bit_definitions{ # Bits within registers. Each list element is a list ref # of [register name, bit name, bit position, comment]. return (get_control_bits(), get_status_bits()); }# Look up a register and bit name(s) in the slave port# bit defs, and return their indices.sub get_slave_port_bits{ my ($reg_name, @bit_names) = @_; my @bits = (); my @reg_spec; { $reg_name eq "control" and do {@reg_spec = get_control_bits(); last;}; $reg_name eq "status" and do {@reg_spec = get_status_bits(); last;}; } ribbit("bad register-bit request '$reg_name'\n") if !@reg_spec; # Where are map and grep when you need them? for my $bit_name (@bit_names) { push @bits, (map {$_->[1] eq $bit_name ? $_->[2] : ()} @reg_spec); } return undef if (0 + @bits != @bit_names); return @bits;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -