-
Notifications
You must be signed in to change notification settings - Fork 20
AXI4 VIP development and integration #688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tchilikov-semify
wants to merge
23
commits into
lowRISC:main
Choose a base branch
from
tchilikov-semify:axi_driver_pr_new
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
e686aa6
[dv,axi] Implement interfaces for the subset of AXI in use
rswarbrick 579723f
[dv,axi] Implement items for AXI read transactions
rswarbrick b59c688
[dv,axi] Implement items for AXI write transactions
rswarbrick 76a5434
[dv,axi] Define an item type to control how we accept responses
rswarbrick 0ab815d
[dv,axi] Implement drivers for the Manager side of write channels
rswarbrick eeea7ec
[dv,axi] Implement drivers for the Manager side of read channels
rswarbrick 8676709
[dv,axi] Define item and monitor for resets on each AXI interface
rswarbrick c1089f4
[dv,axi] Define a router that maps IDs to responses
rswarbrick 078c3ec
[dv,axi] Define axi_agent
rswarbrick 0421d20
[dv,axi] Define a sequence with a single read or write request
rswarbrick 24793d7
[dv,axi] Define sequences with AXI data writes
rswarbrick eecd923
[dv,axi] Define a sequence to consume the AXI write response
rswarbrick 6778405
[dv,axi] Define a sequence to consume AXI read data
rswarbrick 6acf452
[dv,axi] A virtual sequence that does a single write transfer
rswarbrick b3269bf
[dv,axi] A virtual sequence that does a single read transfer
rswarbrick bd9287c
[dv,axi] Allow axi_agent to connect to the register layer
rswarbrick 99d9271
[dv,axi] fix axi handshaking for beat sampling on multi-beat traffic
tchilikov-semify 24b31a4
[dv,axi] add multi-beat transaction support to driver
tchilikov-semify bbb067a
[dv,axi] add clk_rst_if to axi interfaces
tchilikov-semify 78d1c8a
[dv,axi] add monitor to AXI agent
tchilikov-semify 9feaf3d
[dv,axi] use dv_base for vip
tchilikov-semify c62b286
[dv,axi] add axi_vip wrapper to agent
tchilikov-semify 6adb73e
[dv,top] integrate AXI4 monitors in top tb
tchilikov-semify File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| CAPI=2: | ||
| # Copyright lowRISC contributors. | ||
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| name: "lowrisc:dv:axi_agent:0.1" | ||
| description: "AXI agent" | ||
| filesets: | ||
| files_dv: | ||
| depend: | ||
| - lowrisc:dv:dv_utils | ||
| - lowrisc:dv:common_ifs | ||
| - lowrisc:dv:dv_base_agent | ||
| files: | ||
| - axi_read_data_if.sv | ||
| - axi_read_request_if.sv | ||
| - axi_write_data_if.sv | ||
| - axi_write_request_if.sv | ||
| - axi_write_response_if.sv | ||
|
|
||
| - axi_agent_pkg.sv | ||
| - axi_agent_cfg.svh: {is_include_file: true} | ||
|
|
||
| - axi_mon_types.svh: {is_include_file: true} | ||
| - axi_mon_item.svh: {is_include_file: true} | ||
| - axi_mon_write_item.svh: {is_include_file: true} | ||
| - axi_mon_read_item.svh: {is_include_file: true} | ||
| - axi_monitor.svh: {is_include_file: true} | ||
|
|
||
| - axi_reset_item.svh: {is_include_file: true} | ||
| - axi_status_item.svh: {is_include_file: true} | ||
| - axi_txn_request_item.svh: {is_include_file: true} | ||
| - axi_response_accept_item.svh: {is_include_file: true} | ||
| - axi_read_data_item.svh: {is_include_file: true} | ||
| - axi_write_data_item.svh: {is_include_file: true} | ||
| - axi_write_response_item.svh: {is_include_file: true} | ||
|
|
||
| - axi_fixed_write_req_item.svh: {is_include_file: true} | ||
| - axi_fixed_write_rsp_item.svh: {is_include_file: true} | ||
| - axi_fixed_read_req_item.svh: {is_include_file: true} | ||
| - axi_fixed_read_rsp_item.svh: {is_include_file: true} | ||
|
|
||
| - axi_reset_monitor.svh: {is_include_file: true} | ||
|
|
||
| - axi_mgr_write_request_driver.svh: {is_include_file: true} | ||
| - axi_mgr_write_data_driver.svh: {is_include_file: true} | ||
| - axi_mgr_write_response_driver.svh: {is_include_file: true} | ||
| - axi_mgr_read_request_driver.svh: {is_include_file: true} | ||
| - axi_mgr_read_data_driver.svh: {is_include_file: true} | ||
|
|
||
| - axi_response_router.svh: {is_include_file: true} | ||
|
|
||
| - axi_reg_op_item.svh: {is_include_file: true} | ||
| - axi_reg_adapter.svh: {is_include_file: true} | ||
|
|
||
| - axi_agent.svh: {is_include_file: true} | ||
|
|
||
| - seq_lib/axi_mgr_txn_request_seq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_write_data_seq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_write_single_data_seq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_write_listed_data_seq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_write_response_seq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_read_data_seq.svh: {is_include_file: true} | ||
|
|
||
| - seq_lib/axi_mgr_write_fixed_vseq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_write_burst_vseq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_read_fixed_vseq.svh: {is_include_file: true} | ||
| - seq_lib/axi_mgr_read_burst_vseq.svh: {is_include_file: true} | ||
|
|
||
| - seq_lib/axi_mgr_register_layer_vseq.svh: {is_include_file: true} | ||
|
|
||
| file_type: systemVerilogSource | ||
|
|
||
| targets: | ||
| default: | ||
| filesets: | ||
| - files_dv | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,284 @@ | ||
| // Copyright lowRISC contributors | ||
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // The AXI agent. When cfg.is_active it drives all five channels as a manager; when passive it | ||
| // builds only the monitors, which is how it is used to tap subordinate ports. | ||
|
|
||
| class axi_agent extends uvm_agent; | ||
| `uvm_component_utils(axi_agent) | ||
|
|
||
| typedef uvm_sequencer #(axi_reg_op_item) layered_reg_sequencer_t; | ||
|
|
||
| // The agent config object, which allows the testbench to supply virtual interfaces. This can | ||
| // either be set by calling set_cfg() before the build phase, or provided through uvm_config_db. | ||
| local axi_agent_cfg m_cfg; | ||
|
|
||
| // Reset monitor for the shared AXI clock/reset (covers all five channels). | ||
| local axi_reset_monitor m_reset_monitor; | ||
|
|
||
| // The write request channel (AW) | ||
| local axi_mgr_write_request_driver m_write_request_driver; | ||
| local write_request_sequencer_t m_write_request_sequencer; | ||
|
|
||
| // The write data channel (W) | ||
| local axi_mgr_write_data_driver m_write_data_driver; | ||
| local write_data_sequencer_t m_write_data_sequencer; | ||
|
|
||
| // The write response channel (B) | ||
| local axi_mgr_write_response_driver m_write_response_driver; | ||
| local write_response_sequencer_t m_write_response_sequencer; | ||
|
|
||
| // The read request channel (AR) | ||
| local axi_mgr_read_request_driver m_read_request_driver; | ||
| local read_request_sequencer_t m_read_request_sequencer; | ||
|
|
||
| // The read data channel (R) | ||
| local axi_mgr_read_data_driver m_read_data_driver; | ||
| local read_data_sequencer_t m_read_data_sequencer; | ||
|
|
||
| // A response router for writes | ||
| local axi_response_router m_write_response_router; | ||
|
|
||
| // A response router for reads | ||
| local axi_response_router m_read_response_router; | ||
|
|
||
| // A transaction monitor. Built whether the agent is active or passive. | ||
| local axi_monitor m_monitor; | ||
|
|
||
| // A reg adapter. This is stateless, so gets created in build_phase whenever the agent is active. | ||
| // It's useful in conjunction with a layered sequencer (which is created by | ||
| // run_layered_register_vseq and can be retrieved with get_register_layering_sequencer). | ||
| local axi_reg_adapter m_reg_adapter; | ||
|
|
||
| // A sequencer that controls access to an instance of axi_mgr_register_layer_vseq that is | ||
| // currently running. | ||
| local layered_reg_sequencer_t m_layered_reg_sequencer; | ||
|
|
||
| extern function new (string name, uvm_component parent); | ||
| extern function void build_phase(uvm_phase phase); | ||
| extern function void connect_phase(uvm_phase phase); | ||
|
|
||
| // Set m_cfg to the provided cfg. | ||
| // | ||
| // This can only run once, and must be run before the build phase. If it is not run, build_phase | ||
| // will try to get the config object from uvm_config_db. | ||
| extern function void set_cfg(axi_agent_cfg cfg); | ||
|
|
||
| // Get the reset monitor for the shared AXI clock/reset. Can only be called after build_phase. | ||
| extern function axi_reset_monitor get_reset_monitor(); | ||
|
|
||
| // Get the transaction monitor. Can only be called after build_phase. | ||
| extern function axi_monitor get_monitor(); | ||
|
|
||
| // Get the sequencer for the write request channel (AW). Can only be called after build_phase, and | ||
| // the agent must be active. | ||
| extern function write_request_sequencer_t get_write_request_sequencer(); | ||
|
|
||
| // Get the sequencer for the write data channel (W). Can only be called after build_phase, and the | ||
| // agent must be active. | ||
| extern function write_data_sequencer_t get_write_data_sequencer(); | ||
|
|
||
| // Get the sequencer for the write response channel (B). Can only be called after build_phase, and | ||
| // the agent must be active. | ||
| extern function write_response_sequencer_t get_write_response_sequencer(); | ||
|
|
||
| // Get the sequencer for the read request channel (AR). Can only be called after build_phase, and | ||
| // the agent must be active. | ||
| extern function read_request_sequencer_t get_read_request_sequencer(); | ||
|
|
||
| // Get the sequencer for the read data channel (R). Can only be called after build_phase, and the | ||
| // agent must be active. | ||
| extern function read_data_sequencer_t get_read_data_sequencer(); | ||
|
|
||
| // Get the write response router. Can only be called after build_phase, and the | ||
| // agent must be active. | ||
| extern function axi_response_router get_write_response_router(); | ||
|
|
||
| // Get the read response router. Can only be called after build_phase, and the | ||
| // agent must be active. | ||
| extern function axi_response_router get_read_response_router(); | ||
|
|
||
| // Get the reg adapter. Can only be called after build_phase, and the agent must be active. | ||
| extern function axi_reg_adapter get_layered_reg_adapter(); | ||
|
|
||
| // Run the layered register vseq, which shouldn't already be running. | ||
| // | ||
| // This sequence will run forever and its layering sequencer can be retrieved with | ||
| // get_register_layering_sequencer(). | ||
| extern task run_layered_register_vseq(); | ||
|
|
||
| // Get a handle a the sequencer for a layered register vseq that is currently running. If there is | ||
| // not yet one running, this returns null. | ||
| extern function layered_reg_sequencer_t get_register_layering_sequencer(); | ||
| endclass | ||
|
|
||
| function axi_agent::new(string name, uvm_component parent); | ||
| super.new(name, parent); | ||
| endfunction | ||
|
|
||
| function void axi_agent::build_phase(uvm_phase phase); | ||
| super.build_phase(phase); | ||
|
|
||
| if (m_cfg == null && !uvm_config_db#(axi_agent_cfg)::get(this, "", "cfg", m_cfg)) begin | ||
| `uvm_fatal(get_full_name(), "failed to get cfg object from uvm_config_db") | ||
| end | ||
|
|
||
| // One reset monitor for the shared AXI clock/reset (ACLK/ARESETn). As well as broadcasting reset | ||
| // items, this is what maintains m_cfg.in_reset for the channel drivers. | ||
| m_reset_monitor = axi_reset_monitor::type_id::create("m_reset_monitor", this); | ||
| m_reset_monitor.set_cfg(m_cfg); | ||
|
|
||
| // Passive transaction monitor: built in both active and passive agents. | ||
| m_monitor = axi_monitor::type_id::create("m_monitor", this); | ||
| m_monitor.set_cfg(m_cfg); | ||
|
|
||
| if (m_cfg.is_active) begin | ||
| // Create routers for write and read responses | ||
| m_write_response_router = axi_response_router::type_id::create("m_write_response_router", this); | ||
| m_read_response_router = axi_response_router::type_id::create("m_read_response_router", this); | ||
|
|
||
| m_reg_adapter = axi_reg_adapter::type_id::create("m_reg_adapter"); | ||
|
|
||
| // Generate drivers and sequencers for the five channels. | ||
| // The write request channel (AW) | ||
| m_write_request_driver = | ||
| axi_mgr_write_request_driver::type_id::create("m_write_request_driver", this); | ||
| m_write_request_driver.cfg = m_cfg; | ||
| m_write_request_sequencer = | ||
| write_request_sequencer_t::type_id::create("m_write_request_sequencer", this); | ||
|
|
||
| // The write data channel (W) | ||
| m_write_data_driver = axi_mgr_write_data_driver::type_id::create("m_write_data_driver", this); | ||
| m_write_data_driver.cfg = m_cfg; | ||
| m_write_data_sequencer = | ||
| write_data_sequencer_t::type_id::create("m_write_data_sequencer", this); | ||
|
|
||
| // The write response channel (B) | ||
| m_write_response_driver = | ||
| axi_mgr_write_response_driver::type_id::create("m_write_response_driver", this); | ||
| m_write_response_driver.cfg = m_cfg; | ||
| m_write_response_sequencer = | ||
| write_response_sequencer_t::type_id::create("m_write_response_sequencer", this); | ||
|
|
||
| // The read request channel (AR) | ||
| m_read_request_driver = | ||
| axi_mgr_read_request_driver::type_id::create("m_read_request_driver", this); | ||
| m_read_request_driver.cfg = m_cfg; | ||
| m_read_request_sequencer = | ||
| read_request_sequencer_t::type_id::create("m_read_request_sequencer", this); | ||
|
|
||
| // The read data channel (R) | ||
| m_read_data_driver = axi_mgr_read_data_driver::type_id::create("m_read_data_driver", this); | ||
| m_read_data_driver.cfg = m_cfg; | ||
| m_read_data_sequencer = read_data_sequencer_t::type_id::create("m_read_data_sequencer", this); | ||
| end | ||
| endfunction | ||
|
|
||
| function void axi_agent::connect_phase(uvm_phase phase); | ||
| super.connect_phase(phase); | ||
|
|
||
| // If the agent is active, connect drivers to sequencers. | ||
| if (m_cfg.is_active) begin | ||
| m_write_request_driver.seq_item_port.connect(m_write_request_sequencer.seq_item_export); | ||
| m_write_data_driver.seq_item_port.connect(m_write_data_sequencer.seq_item_export); | ||
| m_write_response_driver.seq_item_port.connect(m_write_response_sequencer.seq_item_export); | ||
| m_read_request_driver.seq_item_port.connect(m_read_request_sequencer.seq_item_export); | ||
| m_read_data_driver.seq_item_port.connect(m_read_data_sequencer.seq_item_export); | ||
|
|
||
| // Both response routers observe the same shared reset. | ||
| m_reset_monitor.m_analysis_port.connect(m_write_response_router.reset_imp); | ||
| m_reset_monitor.m_analysis_port.connect(m_read_response_router.reset_imp); | ||
| end | ||
| endfunction | ||
|
|
||
| function void axi_agent::set_cfg(axi_agent_cfg cfg); | ||
| if (m_cfg != null) `uvm_fatal(get_full_name(), "Cannot set cfg: m_cfg is already non-null.") | ||
| m_cfg = cfg; | ||
| endfunction | ||
|
|
||
| function axi_reset_monitor axi_agent::get_reset_monitor(); | ||
| if (m_reset_monitor == null) `uvm_fatal(get_full_name(), "m_reset_monitor is null.") | ||
| return m_reset_monitor; | ||
| endfunction | ||
|
|
||
| function axi_monitor axi_agent::get_monitor(); | ||
| if (m_monitor == null) `uvm_fatal(get_full_name(), "m_monitor is null.") | ||
| return m_monitor; | ||
| endfunction | ||
|
|
||
| function write_request_sequencer_t axi_agent::get_write_request_sequencer(); | ||
| if (m_write_request_sequencer == null) | ||
| `uvm_fatal(get_full_name(), "m_write_request_sequencer is null.") | ||
| return m_write_request_sequencer; | ||
| endfunction | ||
|
|
||
| function write_data_sequencer_t axi_agent::get_write_data_sequencer(); | ||
| if (m_write_data_sequencer == null) | ||
| `uvm_fatal(get_full_name(), "m_write_data_sequencer is null.") | ||
| return m_write_data_sequencer; | ||
| endfunction | ||
|
|
||
| function write_response_sequencer_t axi_agent::get_write_response_sequencer(); | ||
| if (m_write_response_sequencer == null) | ||
| `uvm_fatal(get_full_name(), "m_write_response_sequencer is null.") | ||
| return m_write_response_sequencer; | ||
| endfunction | ||
|
|
||
| function read_request_sequencer_t axi_agent::get_read_request_sequencer(); | ||
| if (m_read_request_sequencer == null) | ||
| `uvm_fatal(get_full_name(), "m_read_request_sequencer is null.") | ||
| return m_read_request_sequencer; | ||
| endfunction | ||
|
|
||
| function read_data_sequencer_t axi_agent::get_read_data_sequencer(); | ||
| if (m_read_data_sequencer == null) | ||
| `uvm_fatal(get_full_name(), "m_read_data_sequencer is null.") | ||
| return m_read_data_sequencer; | ||
| endfunction | ||
|
|
||
| function axi_response_router axi_agent::get_write_response_router(); | ||
| if (m_write_response_router == null) | ||
| `uvm_fatal(get_full_name(), "m_write_response_router is null.") | ||
| return m_write_response_router; | ||
| endfunction | ||
|
|
||
| function axi_response_router axi_agent::get_read_response_router(); | ||
| if (m_read_response_router == null) | ||
| `uvm_fatal(get_full_name(), "m_read_response_router is null.") | ||
| return m_read_response_router; | ||
| endfunction | ||
|
|
||
| function axi_reg_adapter axi_agent::get_layered_reg_adapter(); | ||
| if (m_reg_adapter == null) `uvm_fatal(get_full_name(), "m_reg_adapter is null.") | ||
| return m_reg_adapter; | ||
| endfunction | ||
|
|
||
| task axi_agent::run_layered_register_vseq(); | ||
| axi_mgr_register_layer_vseq layer_vseq; | ||
|
|
||
| if (m_layered_reg_sequencer != null) begin | ||
| `uvm_fatal(get_full_name(), "Overlapping runs of layered register vseq.") | ||
| end | ||
|
|
||
| m_layered_reg_sequencer = layered_reg_sequencer_t::type_id::create("m_layered_reg_sequencer", this); | ||
|
|
||
| layer_vseq = axi_mgr_register_layer_vseq::type_id::create("layer_vseq"); | ||
|
|
||
| layer_vseq.set_sequencers(m_layered_reg_sequencer, | ||
| m_write_request_sequencer, | ||
| m_write_data_sequencer, | ||
| m_write_response_sequencer, | ||
| m_read_request_sequencer, | ||
| m_read_data_sequencer); | ||
| layer_vseq.set_response_routers(m_read_response_router, m_write_response_router); | ||
|
|
||
| layer_vseq.start(null); | ||
|
|
||
| // Because layer_vseq never completes, we don't expect to get her. | ||
| `uvm_fatal(get_full_name(), "Instance of axi_mgr_register_layer_vseq ran to completion.") | ||
| endtask | ||
|
|
||
| function axi_agent::layered_reg_sequencer_t axi_agent::get_register_layering_sequencer(); | ||
| return m_layered_reg_sequencer; | ||
| endfunction |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // Copyright lowRISC contributors | ||
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // The configuration for an agent driving the interfaces for AXI (AW, W, B, AR, R). | ||
| // | ||
| // This carries only the AXI-specific interface handles. Everything else (is_active, en_cov, ...) | ||
| // is an inherited dv_base_agent_cfg field and is set directly by whoever builds the cfg. The | ||
| // instance is named after its VIP instantiation, so get_name() identifies which port it belongs to. | ||
|
|
||
| class axi_agent_cfg extends dv_base_agent_cfg; | ||
| `uvm_object_utils(axi_agent_cfg) | ||
|
|
||
| // Interfaces | ||
| virtual clk_rst_if clk_rst_vif; // ACLK/ARESETn | ||
|
|
||
|
tchilikov-semify marked this conversation as resolved.
|
||
| virtual axi_write_request_if write_request_vif; | ||
| virtual axi_write_data_if write_data_vif; | ||
| virtual axi_write_response_if write_response_vif; | ||
| virtual axi_read_request_if read_request_vif; | ||
| virtual axi_read_data_if read_data_vif; | ||
|
|
||
| extern function new(string name = "axi_agent_cfg"); | ||
| endclass | ||
|
|
||
| function axi_agent_cfg::new(string name = "axi_agent_cfg"); | ||
| super.new(name); | ||
| endfunction | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.