📄 eth_subenv.sv
字号:
// // -------------------------------------------------------------// Copyright 2004-2008 Synopsys, Inc.// All Rights Reserved Worldwide// // Licensed under the Apache License, Version 2.0 (the// "License"); you may not use this file except in// compliance with the License. You may obtain a copy of// the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in// writing, software distributed under the License is// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR// CONDITIONS OF ANY KIND, either express or implied. See// the License for the specific language governing// permissions and limitations under the License.// -------------------------------------------------------------//`ifndef ETH_SUBENV__SV`define ETH_SUBENV__SV`include "vmm_ral.sv"`include "vmm_subenv.sv"`include "mii.sv"class oc_eth_subenv_cfg; rand mii_cfg mii; rand bit [47:0] dut_addr; rand bit [ 7:0] n_tx_bd; rand bit [15:0] max_frame_len; // Stop when both limits are reached rand int unsigned run_for_n_frames; bit [31:0] avail_txbd[$]; vmm_mam_region dma_bfrs[*]; // Indexed by BD offset constraint test_cfg_valid { mii.is_10Mb == 0; mii.full_duplex == 0; n_tx_bd > 1; n_tx_bd <= 128; } constraint coverage_driven { max_frame_len inside {1500, 1518, 'h0600}; } constraint reasonable { run_for_n_frames < 100; } function new(); this.mii = new; endfunction: new function string psdisplay(string prefix = ""); $sformat(psdisplay, "%sDUT MAC Addr: %h.%h.%h.%h.%h.%h", prefix, dut_addr[47:40], dut_addr[39:32], dut_addr[31:24], dut_addr[23:16], dut_addr[15:8], dut_addr[7:0]); $sformat(psdisplay, "%s\n%s%0d TxBD\n", psdisplay, prefix, this.n_tx_bd); $sformat(psdisplay, "%s\n%s%0d bytes max frame length", psdisplay, prefix, this.max_frame_len); $sformat(psdisplay, "%s\n%sfor %0d Tx frames", psdisplay, prefix, this.run_for_n_frames); endfunctionendclass: oc_eth_subenv_cfg//// Self-Checking Structure//class scoreboard; oc_eth_subenv_cfg cfg; vmm_log log; eth_frame to_phy_side[$]; function new(string inst, oc_eth_subenv_cfg cfg); this.cfg = cfg; this.log = new("Scoreboard", inst); endfunction: new function void sent_from_mac_side(eth_frame fr); // Is it too long? if (fr.byte_size() > this.cfg.max_frame_len) return; // Will it be accepted by the PHY-side MAC layer? if (fr.fcs) return;// Not if it has a bad FCS to_phy_side.push_back(fr); endfunction function void received_by_phy_side(eth_frame fr); eth_frame exp; string diff; if (this.cfg.run_for_n_frames > 0) begin this.cfg.run_for_n_frames--; end if (this.to_phy_side.size() == 0) begin `vmm_error(this.log, $psprintf("Unexpected frame received on PHY side (none expected):\n%s", fr.psdisplay(" "))); return; end exp = this.to_phy_side.pop_front(); if (!fr.compare(exp, diff)) begin `vmm_error(this.log, $psprintf("Unexpected frame received on PHY side: %s:\n%s\n%s", diff, fr.psdisplay(" Actual: "), exp.psdisplay(" Expect: "))); return; end `vmm_note(this.log, $psprintf("Expected frame received on PHY side (%0d left):\n%s", this.cfg.run_for_n_frames, fr.psdisplay(" "))); endfunctionendclass: scoreboardtypedef class frmwr_emulator;virtual class frmwr_emulator_callbacks extends vmm_xactor_callbacks; virtual task pre_frame_tx(frmwr_emulator xactor, ref eth_frame frame, ref bit drop); endtask virtual function void post_frame_rx(frmwr_emulator xactor, /*const*/ eth_frame frame); endfunctionendclass: frmwr_emulator_callbacksclass frmwr_emulator extends vmm_xactor; eth_frame_channel tx_chan; scoreboard sb; local oc_eth_subenv_cfg cfg; local ral_block_oc_ethernet ral; local wb_ram ram; local vmm_mam dma_mam; local bit [31:0] busy_txbd[$]; function new(string instance, int unsigned stream_id = -1, oc_eth_subenv_cfg cfg, eth_frame_channel tx_chan, scoreboard sb, ral_block_oc_ethernet ral, wb_ram dma_ram, vmm_mam dma_mam); super.new("FRMWR Emulator", instance, stream_id); this.tx_chan = tx_chan; this.sb = sb; this.cfg = cfg; this.ral = ral; this.ram = dma_ram; this.dma_mam = dma_mam; this.reset_xactor(); endfunction: new function void reset_xactor(reset_e typ = SOFT_RST ); super.reset_xactor(typ); tx_chan.flush(); endfunction: reset_xactor virtual task main(); fork super.main(); this.tx_driver(); join endtask: main local task tx_driver(); logic [ 7:0] bytes[]; forever begin eth_frame fr; bit drop = 0; vmm_rw::status_e status; // Wait for a free Tx buffer if none available wait (this.cfg.avail_txbd.size() > 0); this.wait_if_stopped_or_empty(this.tx_chan); this.tx_chan.activate(fr); `vmm_callback(frmwr_emulator_callbacks, pre_frame_tx(this, fr, drop)); if (!drop) begin vmm_mam_region bfr; int len; bit wrap = 0; int bd_addr; this.tx_chan.start(); bfr = dma_mam.request_region(fr.byte_size()); // Write the frame in the DMA RAM `vmm_trace(this.log, $psprintf("Buffering TX Frame at 'h%h:\n%s", bfr.get_start_offset(), fr.psdisplay(" "))); // We need full DWORDs len = fr.byte_size(); bytes = new [len + (4 - len % 4)]; fr.byte_pack(bytes); for (int i = 0; i < len; i += 4) begin this.ram.write(bfr.get_start_offset() + i, {bytes[i], bytes[i+1], bytes[i+2], bytes[i+3]}); end // Set the TxBD accordingly `vmm_debug(this.log, $psprintf("Updating TxBD at 'h%h", bd_addr)); bd_addr = this.cfg.avail_txbd.pop_front(); ral.TxBD[bd_addr].PTR.write(status, bfr.get_start_offset()); if (status !== vmm_rw::IS_OK) begin `vmm_error(this.log, $psprintf("Unable to set TxBD[%0d].PTR", bd_addr)); end cfg.dma_bfrs[bd_addr] = bfr; ral.TxBD[bd_addr].LEN.set(len); ral.TxBD[bd_addr].RD.write(status, 1); if (status !== vmm_rw::IS_OK) begin `vmm_error(this.log, $psprintf("Unable to mark TxBD[%0d] ready", bd_addr)); end this.busy_txbd.push_back(bd_addr); this.tx_chan.complete(); this.sb.sent_from_mac_side(fr); end // drop this.tx_chan.remove(); end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -