📄 em_dma.pm
字号:
my @contents = (); my @muxtable; # Get transaction bit names, least-significant first. my @trans_bit_names = get_transaction_size_bit_names(); # Optimization alert: it sure seems that I ought to be able to drop the all-1's # case from the byteenable mux. for (my $trans_size = 1; $trans_size <= $num_byteenables; $trans_size <<= 1) { # Each time through this loop, push a pair of values into muxtable. # Value 1: selector (simply the name of the transaction size bit for # this transaction size). # Value 2: an expression for the byteenable signal, depending on # the address and the current transaction size. my $trans_bit_name = pop @trans_bit_names; ribbit("unexpected error") if !$trans_bit_name; next if !is_transaction_allowed($trans_bit_name); ribbit("ran out of transaction bit names!") if !$trans_bit_name; my $be_expression; # Log(multiplier) tells you how many address bits the elements of the # byteenable expression depend on. my $num_important_address_bits = log2($num_byteenables / $trans_size); if ($num_important_address_bits == 0) { $be_expression = sprintf("%d'b%s", $num_byteenables, '1' x $num_byteenables); } else { my @terms; my $address_msb = log2($num_byteenables) - 1; my $address_lsb = $address_msb - $num_important_address_bits + 1; my $addr_sel = $address_msb == $address_lsb ? "$address_msb" : "$address_msb : $address_lsb"; my $addr_sel_for_signal; ($addr_sel_for_signal = $addr_sel) =~ s/ : /_to_/; my $sig_name_prefix = "wa_$addr_sel_for_signal\_is_"; my %sel_signals; # Avoid redundant signal names. for my $sel (reverse(0 .. $num_byteenables - 1)) { my $sig_name = "$sig_name_prefix@{[$sel >> $address_lsb]}"; if (!defined($sel_signals{$sig_name})) { # Create the signal. push @contents, e_assign->new({ lhs => [$sig_name, 1,], rhs => sprintf("($port_prefix\_address\[$addr_sel] == %d'h%X)", $num_important_address_bits, $sel >> $address_lsb), }); # Remember that this signal has been created. $sel_signals{$sig_name} = 1; } push @terms, $sig_name; } # Concatenate all terms. $be_expression = "{@{[join(', ', @terms)]}}"; } if (get_allowed_transactions() > 1) { push @muxtable, ($trans_bit_name, $be_expression); } else { # Instead of a mux, a simple assignment. push @contents, ( e_assign->new({ lhs => "$port_prefix\_byteenable", rhs => $be_expression, }), ); } } if (get_allowed_transactions() > 1) { push @contents, ( e_mux->new({ lhs => "$port_prefix\_byteenable", table => \@muxtable, type => "and_or", }), ); } $byteenable_module->add_contents(@contents); return e_instance->new({module => $byteenable_module});}sub get_write_master_ports{ my ($Options, $burst_enable, $max_burstcount_width) = @_; my $port_prefix = 'write'; my @ports = get_master_ports($Options, $port_prefix); # SPR 12225. if (has_byteenables($Options)) { my $num_byteenables = $Options->{writedatawidth} / 8; push @ports, ( e_port->new({ name => $port_prefix . "_byteenable", width => $num_byteenables, direction => "output", type => 'byteenable', }), ); } push @ports, ( e_port->new({ name => $port_prefix . "_address", direction => "output", width => $Options->{writeaddresswidth}, type => 'address', }), e_port->new({ name => $port_prefix . "_writedata", direction => "output", width => $Options->{writedatawidth}, type => 'writedata', }), e_port->new({ name => $port_prefix . "_write_n", direction => "output", type => 'write_n', }), ); push @ports, ( e_port->new({ name => $port_prefix . "_burstcount", type => "burstcount", direction => "output", width => "$max_burstcount_width" }) ) if ($burst_enable); return @ports;}sub get_read_master_ports{ my ($Options, $burst_enable, $max_burstcount_width) = @_; my $port_prefix = 'read'; my @ports = get_master_ports($Options, $port_prefix); push @ports, ( e_port->new({ name => $port_prefix . "_address", direction => "output", width => $Options->{readaddresswidth}, type => 'address', }), e_port->new({ name => $port_prefix . "_readdata", direction => "input", width => $Options->{readdatawidth}, type => 'readdata', }), e_port->new({ name => $port_prefix . "_read_n", direction => "output", type => 'read_n', }), e_port->new({ name => $port_prefix . "_readdatavalid", direction => "input", type => 'readdatavalid', }), e_port->new({ name => $port_prefix . "_flush", direction => "output", type => 'flush', }), ); push @ports, ( e_port->new({ name => $port_prefix . "_burstcount", type => "burstcount", direction => "output", width => "$max_burstcount_width" }) ) if ($burst_enable); return @ports;}sub get_master_ports{ my ($Options, $port_prefix) = @_; my @master_ports = ( e_port->new({ name => $port_prefix . "_chipselect", direction => "output", type => 'chipselect', }), e_port->new({ name => $port_prefix . "_waitrequest", direction => "input", type => 'waitrequest', }), e_port->new({ name => $port_prefix . "_endofpacket", direction => "input", type => 'endofpacket', }) ); return @master_ports;}sub make_fifo{ my ($top_module, $Options) = @_; $top_module->add_contents( e_assign->new([ "flush_fifo", "~d1_done_transaction & done_transaction" ]), ); my $fifo_module = e_fifo->new({ device_family => $top_module->project()->device_family(), name_stub => $top_module->name(), data_width => $Options->{fifodatawidth}, fifo_depth => $Options->{fifo_depth}, flush => "flush_fifo", full_port => 0, p1_full_port => 1, empty_port => 1, implement_as_esb => $Options->{fifo_in_logic_elements} ? 0 : 1, Read_Latency => $Options->{fifo_read_latency}, }); return $fifo_module;}sub make_fsm{ my ( $name, $go, $p1_done, $mem_wait, $p1_fifo_stall, $select, $access_n, $inc, $fifo_access, $extra_latency, ) = @_; my $fsm = e_fsm->new({ name => $name, start_state => "idle", }); # Registered outputs for memory control. my $p1_select = "p1_" . $select; $fsm->add_contents( e_signal->new({ name => $p1_select, never_export => 1, }), e_assign->new({ lhs => e_signal->new({name => $access_n, export => 1,}), rhs => "~$select", }), e_register->new({ delay => 1 + $extra_latency, in => $p1_select, out => e_signal->new({name => $select, never_export => 1,}), }), ); # Optional fifo-access output - same as $inc. if ($fifo_access) { $fsm->add_contents( e_signal->new({name => $inc, export => 1,}), e_assign->new({ lhs => e_signal->new({name => $fifo_access,}), rhs => "$name\_access & ~$mem_wait", }), ); } $fsm->OUTPUT_DEFAULTS({ $p1_select => 0, }); $fsm->OUTPUT_WIDTHS({ $p1_select => 1, }); $fsm->add_state( "idle", [ {$go => 0,}, "idle", {} ], [ {$p1_done => 1,}, "idle", {} ], # Loop while the fifo forces a stall. [ {$p1_fifo_stall => 1,}, "idle", {} ], [ # Go off to do an access. # Note extra latency here: p1_fifo_stall = 0 means that it would # be safe to do an access to a latent peripheral now, because there # will be room by the time the new data arrives. But, since select # is registered, it takes one more cycle to make the request. Seems # like this could be optimized, but it might cause trouble with # reads of non-latent slaves. { $go => 1, $p1_done => 0, $p1_fifo_stall => 0, }, "access", {$p1_select => 1,}, ], ); $fsm->add_state( "access", # FIFO stalls: wait in idle. [ {$p1_fifo_stall => 1, $mem_wait => 0,}, "idle", {}, ], # When finished, go back to idle. [ {$p1_done => 1, $mem_wait => 0,}, "idle", {}, ], # If the memory says "wait", wait! [ {$mem_wait => 1, }, "access", {$p1_select => 1,}, ], # Streaming right along... do another access. [ { $mem_wait => 0, $p1_fifo_stall => 0, $p1_done => 0, }, "access", {$p1_select => 1,}, ], ); # Special case: dumb e_fsm can't figure out how to make this expression # without a combinational logic loop, but I can. $fsm->add_contents( e_assign->new({ # comment => " $inc is active in state $name\_access, unless $mem_wait is high.", lhs => [$inc, 1,], rhs => "$select & ~$mem_wait", }), ); return $fsm;}sub make_write_machine{ my ($Options, $name) = @_; # Given # # write_waitrequest # fifo_datavalid # # generate # # fifo_read # inc_write # mem_write_n # write_select # my $write_fsm_module = e_module->new({ name => $name, }); $write_fsm_module->add_contents( e_assign->new({ lhs => e_signal->new({name => "write_select", export => 1,}), rhs => 'fifo_datavalid & ~d1_enabled_write_endofpacket', }), e_assign->new({ lhs => e_signal->new({name => "mem_write_n", export => 1,}), rhs => "~write_select", }), e_assign->new({ lhs => e_signal->new({name => "fifo_read", export => 1,}), rhs => "write_select & ~write_waitrequest", }), e_assign->new({ lhs => "inc_write", rhs => "fifo_read", }), ); return $write_fsm_module;}sub make_fsms{ my ($top_module, $Options) = @_; # Create three state machines: # 1) reads memory # 2) writes to FIFO when data arrives # 3) reads from FIFO, writes to memory. # READ FSM (read memory, write to FIFO): # Inputs: # go # p1_done_read # read_waitrequest # p1_fifo_full
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -