⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 em_dma.pm

📁 altera_avalon_dma.rar大家快下啊
💻 PM
📖 第 1 页 / 共 5 页
字号:
sub get_slave_port_register_indices{  my ($Options, @names) = @_;  my @regs = get_slave_port_registers($Options);  my @indices;    for my $name (@names)  {    my $register_index = '';    my $i = 0;    for my $reg (@regs)    {      if ($reg->[1] eq $name)      {        $register_index = $i;        last;      }            $i++;    }        # Push the index if found, otherwise null string.    push @indices, $register_index;  }    return @indices;}# Reads are always done at full slave data width - there's no analogy to the# byte enable signals of a write access.  So, for cases like##  8/16-bit peripheral streaming to 32-bit memory#  32/16-bit memory streaming to 16/8 bit peripheral# # where the FIFO is written with data of the width of the transaction, that# is, the minimum size of read and write, we need a data mux.sub make_read_master_data_mux{  # If all slaves on the read side are non-latent, then the mux select is just  # the low bits of the read address.  If any slave is latent, then load a  # 2-bit counter on read_address write, and increment it synchronously on  # readdatavalid. What the heck, I'll throw this stuff in in all cases.  # Future optimization: trim the unnecessary address counter if all read  # slaves have latency=0.  my ($Options, $module, $project) = @_;  my $read_data_mux_module = e_module->new({    name => $module->name() . "_read_data_mux",  });  my @contents = ();  my $mux_select_bits = log2($Options->{readdatawidth} / 8);  if (get_allowed_transactions() == 1 or $mux_select_bits == 0)  {    # Special case: only one possible transaction.  Don't make a mux (it    # would be a pretty simple mux, selected by the transaction-size bit    # alone).  I can make it even simpler: a plain old assignment.    # Also, there's a degenerate case: readdatawidth (and fifodatawidth) are 8.    # Only byte transactions are possible. No mux is necessary!    push @contents,       e_assign->new({        lhs => "fifo_wr_data[@{[$Options->{readdatawidth} - 1]} : 0]",        rhs =>  "read_readdata[@{[$Options->{readdatawidth} - 1]} : 0]",      });  }  else  {    # General case:     my $address_select_range = "0";    if ($mux_select_bits > 1)    {      $address_select_range = "@{[$mux_select_bits - 1]}: 0";    }    push @contents, (      e_signal->new({name => "readdata_mux_select", width => $mux_select_bits,}),    );    if ($Options->{max_read_latency} == 0)    {      push @contents, (        e_assign->new(["readdata_mux_select", "read_address[$address_select_range]",]),      );    }    else    {      # Grab the memory map.        my @reg_info = get_slave_port_registers($Options);      # Get the locations of the control, readaddress and length registers.      my ($control_index, $readaddress_index, $length_index) =        get_slave_port_register_indices($Options, "control", "readaddress", "length");      ribbit("No control register!") if ('' eq $control_index);      ribbit("No readaddress register!") if ('' eq $readaddress_index);      ribbit("No readaddress register!") if ('' eq $length_index);      push @contents, (        e_assign->new([          e_signal->new(["control_write"]), $reg_info[$control_index]->[3]        ]),      );      push @contents, (        e_assign->new([          e_signal->new(["length_write"]), $reg_info[$length_index]->[3]        ]),      );      # Look up the bit number of the go bit in the control register.      my ($go_bit_pos) = get_slave_port_bits("control", "go");      ribbit("no go bit!\n") if (!defined($go_bit_pos));      # Get the indices of the transaction size bits within the control register.      my @trans_bits = get_transaction_size_bit_indices();      # Figure out the reset value of the bits of the readaddress register      # which correspond to the transaction-size bits.      my $readaddress_reset_val = $reg_info[$readaddress_index]->[7];      $readaddress_reset_val =~ s/([0-9]*)\'h/0x/g;      $readaddress_reset_val = hex($readaddress_reset_val);      if ($readaddress_reset_val)      {        # If non-zero reset value, AND against the mux select bits.        $readaddress_reset_val .=          sprintf(" & %d\'b%s", $mux_select_bits, '1' x $mux_select_bits);      }              # Right, so how about a loadable counter, incremented by the current      # transaction size when readdatavalid is true?  This counter resets      # to the same value as the readaddress register, and is written with the      # readaddress register contents whenever the go bit is written.       # Whenever readdatavalid is true, the register increments by the same      # value that the readaddress increments by.      # SPR 170912: also reset the counter when the length register is written.      push @contents, (        e_mux->new({          lhs => e_signal->new({name => "read_data_mux_input", width => $mux_select_bits,}),          table => [            "control_write && dma_ctl_writedata[$go_bit_pos] || length_write",            "readaddress[1:0]",            "read_readdatavalid",            "readdata_mux_select + readaddress_inc",          ],          default => "readdata_mux_select",          type           => "priority",        }),        e_register->new({          comment =>            " Reset value: the transaction size bits of the read address reset value.",          out => "readdata_mux_select",          in => "read_data_mux_input[$address_select_range]",          async_value => $readaddress_reset_val,        }),      );    }    # Define a read-data mux.  This routes data from the read data    # bus into the fifo.  It's possible that the read data bus is    # wider than the fifo, but the fifo cannot be wider than the    # read data bus (the fifo data width is the minimum of the    # read- and write-data bus widths).    # The structure of the mux depends on:    # $Options->{readdatawidth}    # $Options->{fifodatawidth}    # The mux select input is based on    # readdata_mux_select and the current transaction size.    # Loop over each allowed transaction size.    # Optimization alert: disallow small transaction sizes to avoid generating    # lots of mux logic.    #     # Optimization alert: generate <fifodatawidth> 1-bit muxes.  The LSBs of the     # current mux have many terms which the MSBs lack, which I try to express    # as don't cares, but I'm not sure how well the synthesizer will take    # this info into account.    push @contents, e_signal->new({      name => "fifo_wr_data",      width => $Options->{fifodatawidth},    });    # Get transaction bit names in least-significant order first.    my @trans_names = reverse get_transaction_size_bit_names();    my $msb = $Options->{fifodatawidth} - 1;    my $lsb = $Options->{fifodatawidth} / 2;    while (1)    {      # Make a mux for fifo_wr_data[$msb : $lsb]      my @mux_table;      for my $trans_index (0 .. @trans_names - 1)      {        my $trans_name = $trans_names[$trans_index];        next if not is_transaction_allowed($trans_name);        my $trans_size_in_bits = transaction_size_in_bits($trans_name);        next if $trans_size_in_bits <= $lsb;        my $multiple = $Options->{readdatawidth} / $trans_size_in_bits;        my $mux_select_msb = log2($Options->{readdatawidth} / 8) - 1;        my $mux_select_lsb = $trans_index;        my $mux_select;        if ($mux_select_msb >= $mux_select_lsb)        {          $mux_select = "readdata_mux_select[$mux_select_msb : $mux_select_lsb]";        }        for my $i (0 .. $multiple - 1)        {          my $select = $trans_name;          if ($mux_select)          {            $select .= " & ($mux_select == $i)";          }          my $basic_selection =             "read_readdata[@{[$i * $trans_size_in_bits + $msb]} : @{[$i * $trans_size_in_bits + $lsb]}]";          my $full_selection;          my $excess_bits = $Options->{fifodatawidth} - $trans_size_in_bits;          my $top_half = sprintf("read_readdata[%d : %d]",             $Options->{fifodatawidth} - 1, $Options->{fifodatawidth} / 2);          my $dont_care_part;          my $dont_care_bits = $excess_bits - $Options->{fifodatawidth} / 2;          if ($dont_care_bits > 0)          {            $dont_care_part =              sprintf("{%d{1'b%s}}, ", $dont_care_bits, $::g_dont_care_value);          }          push @mux_table, ($select, $basic_selection);        }      }      # If the mux table contains only one term, emit a straight assignment instead.      if (@mux_table == 2)      {        push @contents,           e_assign->new({            lhs  => "fifo_wr_data[$msb : $lsb]",            rhs => $mux_table[1],          });      }      else      {        # More than one term: make a real mux.        push @contents,           e_mux->new({            lhs  => "fifo_wr_data[$msb : $lsb]",            type => "and_or",            table => \@mux_table,          });      }      # Get the msb and lsb for the next segment.  This code is oddly complicated,      # due to the fact that lsb = 0 is a weird special case in the sequence, which       # goes 64, 32, 16, 8, ... -> 0 instead of 4 <-      last if $lsb == 0;      $msb -= $lsb;      $lsb /= 2;      $lsb = 0 if $lsb < 8;    }  }  $read_data_mux_module->add_contents(@contents);  return e_instance->new({module => $read_data_mux_module});}sub make_registers{  my ($module, $Options) = @_;  my $burst_enable = $Options->{burst_enable};Progress("DMA burst enable state: $burst_enable") if $Options->{europa_debug};  # Grab the memory map.    my @reg_info = get_slave_port_registers($Options);  # Make a control register process for each register descriptor  # in the list.  my @write_regdesc;  my $length_index = -1;  for my $i (0 .. @reg_info - 1)  {    my $reg = $reg_info[$i];    # Special case: don't generate the status register: it's composed    # of individual register bits, which are assigned to the signal    # 'status' for export.    if ($reg->[1] eq "status")    {      # We don't need a register for status (it's composed of various      # register bits) but while we're here, make a handy status-write      # signal.      $module->add_contents(        e_assign->new({          lhs => e_signal->new(["status_register_write"]),          rhs =>            "dma_ctl_chipselect & ~dma_ctl_write_n & (dma_ctl_address == $i)",        }),      );      next;    }    # Special case: "reserved3" is just an alias of "control", and has    # no hardware existence except for an extra address that decodes to    # control.    next if ($reg->[1] eq "reserved3");    # The writelength register isn't visible to the programmer, but it    # behaves like an ordinary register in all other ways. It's very    # similar to the length register, so make a copy of that     # info when that register passes by.    if ($reg->[1] eq "length")    {      $length_index = $i;      @write_regdesc = @{$reg};    }    $module->add_contents(      control_register(@{$reg})    );  }  ribbit ("length not found: can't make writelength register!\n")    if (!@write_regdesc or ($length_index == -1));  # Now make some small modifications to writelength.  $write_regdesc[0] = " write master length";  $write_regdesc[1] = "writelength";  $write_regdesc[5] = "inc_write && (!writelength_eq_0)";  $write_regdesc[6] = " - " . get_transaction_size_expression();  $module->add_contents(control_register(@write_regdesc));  # In the FSMs, I need to know one cycle ahead of time if the  # length and writelength registers will be 0.  Make handy signals  # for that condition.  $module->add_contents(    e_assign->new({      lhs => e_signal->new({name => "p1_writelength_eq_0",}),      rhs => "$write_regdesc[5] && ((writelength $write_regdesc[6]) == 0)",    }),    e_assign->new({      lhs => e_signal->new({name => "p1_length_eq_0",}),      rhs => "$reg_info[$length_index]->[5] && " .        "((length $reg_info[$length_index]->[6]) == 0)",    }),  );  # Create registered "length equals 0" and "writelength equals 0" signals.  # Note: if the user writes 0 into the length register, length_eq_0 will   # momentarily go false.  # Ugh. As always, remember to convert those reset values from verilog constants  # into perl numbers.  my $length_reset_as_number;  ($length_reset_as_number = $reg_info[$length_index]->[7]) =~ s/[0-9]*\'h/0x/g;  $length_reset_as_number = eval($length_reset_as_number);  my $writelength_reset_as_number;  ($writelength_reset_as_number = $write_regdesc[7]) =~ s/[0-9]*\'h/0x/g;  $writelength_reset_as_number = eval($writelength_reset_as_number);  $module->add_contents(    e_register->new({      out => "length_eq_0",      async_value => 0 + ($length_reset_as_number == 0),      sync_set => "p1_length_eq_0",      sync_reset => $reg_info[$length_index]->[3],    }),    e_register->new({      out => "writelength_eq_0",      async_value => 0 + ($writelength_reset_as_number == 0),      sync_set => "p1_writelength_eq_0",      sync_reset => $write_regdesc[3],    }),  );  # The increment next values of the read and write addresses are  # determined as follows:  # if the addr_constant bit is 1, the increment is 0.  #  otherwise,   # if the increment register is 0, the   #   increment is determined by the control register bits  #   {word, hw, byte}.  #  otherwise  # the increment value is set by the increment register.  # Most users will never need to override the increment values  # with readincov and writeincov.  Therefore, if (read|write)incovwidth  # is set to 0, that register doesn't get implemented.  To handle both  # cases and allow Leo to remove lots of logic, created a  # (read|write)incov_eq_0 register which is  # - constant 1, if the register has 0 width  # - otherwise, latched with the appropriate value when the register is  #   written.  if ($Options->{readincovwidth})  {    my $is0_p1_rhs;    my $readinc_index = -1;    map {      $readinc_index = $_ if ($reg_info[$_]->[1] eq "readincov")    } (0 .. -1 + @reg_info);    ribbit("no readincov register!") if $readinc_index == -1;    my $incov_clken = $reg_info[$readinc_index]->[3];    my $reset_val;    ($reset_val = $reg_info[$readinc_index]->[7]) =~ s/[0-9]*\'h/0x/g;    $reset_val = eval($reset_val);    my $async_val = $reset_val == 0 ? "1" : "0";    $is0_p1_rhs =      sprintf("dma_ctl_writedata[%d:0] == 0", $Options->{readincovwidth} - 1);    $module->add_contents(      e_assign->new({        lhs => e_signal->new({          name => "p1_readincov_eq_0", never_export => 1,        }),        rhs => $is0_p1_rhs,      }),      e_register->new({        out => "readincov_eq_0",        in => "p1_readincov_eq_0",        enable => $incov_clken,        async_value => $async_val,      }),    );  }  if ($Options->{writeincovwidth})  {    my $is0_p1_rhs;    my $writeinc_index = -1;    map {      $writeinc_index = $_ if ($reg_info[$_]->[1] eq "writeincov")    } (0 .. -1 + @reg_info);    ribbit("no writeincov register!") if $writeinc_index == -1;    my $incov_clken = $reg_info[$writeinc_index]->[3];    my $reset_val;    ($reset_val = $reg_info[$writeinc_index]->[7]) =~ s/[0-9]*\'h/0x/g;    $reset_val = eval($reset_val);    my $async_val = $reset_val == 0 ? "1" : "0";    $is0_p1_rhs = sprintf("dma_ctl_writedata[%d:0] == 0",      $Options->{writeincovwidth} - 1);    $module->add_contents(

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -