From a8a9e2aa8b99b881fd0f9d8afc065c77731799ac Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Mon, 10 Feb 2020 22:08:30 -0600 Subject: [PATCH 01/18] Initial commit for tester prototype --- example/tests/shared/__init__.py | 23 + example/tests/shared/python_like_apis.py | 1 - example/tests/tester_test.py | 224 ++++++++++ python/origen/__init__.py | 23 +- python/origen/application.py | 3 + python/origen/tester.py | 26 ++ rust/origen/meta/src/id_getters_derive.rs | 9 +- rust/origen/src/core/mod.rs | 1 + rust/origen/src/core/tester.rs | 413 +++++++++++++++++ rust/origen/src/lib.rs | 8 + rust/origen/src/testers/igxl/.gitkeep | 0 rust/origen/src/testers/mod.rs | 1 + rust/origen/src/testers/v93k/mod.rs | 18 + rust/pyapi/Cargo.toml | 2 +- rust/pyapi/src/lib.rs | 4 + .../src/meta/py_like_apis/dict_like_api.rs | 3 +- .../src/meta/py_like_apis/list_like_api.rs | 3 +- rust/pyapi/src/tester.rs | 418 ++++++++++++++++++ rust/pyapi/src/timesets.rs | 4 +- rust/pyapi/src/timesets/timeset.rs | 6 + rust/pyapi/src/timesets/timeset_container.rs | 11 +- 21 files changed, 1182 insertions(+), 19 deletions(-) create mode 100644 example/tests/shared/__init__.py create mode 100644 example/tests/tester_test.py create mode 100644 python/origen/tester.py create mode 100644 rust/origen/src/core/tester.rs create mode 100644 rust/origen/src/testers/igxl/.gitkeep create mode 100644 rust/origen/src/testers/mod.rs create mode 100644 rust/origen/src/testers/v93k/mod.rs create mode 100644 rust/pyapi/src/tester.rs diff --git a/example/tests/shared/__init__.py b/example/tests/shared/__init__.py new file mode 100644 index 00000000..5a64f8b2 --- /dev/null +++ b/example/tests/shared/__init__.py @@ -0,0 +1,23 @@ +import pytest +import origen, _origen # pylint: disable=import-error + +@pytest.fixture +def clean_eagle(): + origen.app.instantiate_dut("dut.eagle") + assert origen.dut + return origen.dut + +@pytest.fixture +def clean_falcon(): + origen.app.instantiate_dut("dut.falcon") + assert origen.dut + return origen.dut + +@pytest.fixture +def clean_tester(): + assert origen.tester + origen.tester.reset() + assert origen.tester.targets == [] + assert len(origen.tester.ast) == 0 + assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7"] + assert origen.tester.timeset is None \ No newline at end of file diff --git a/example/tests/shared/python_like_apis.py b/example/tests/shared/python_like_apis.py index 2ce49e39..74deec85 100644 --- a/example/tests/shared/python_like_apis.py +++ b/example/tests/shared/python_like_apis.py @@ -30,7 +30,6 @@ def parameterize(self): def boot(self): self.expected = self.Expected(self.parameterize()) self.dut = self.boot_dict_under_test() - print("Booted!") def test_keys(self, boot): assert self.dut.keys diff --git a/example/tests/tester_test.py b/example/tests/tester_test.py new file mode 100644 index 00000000..879285a0 --- /dev/null +++ b/example/tests/tester_test.py @@ -0,0 +1,224 @@ +import pytest +import origen, _origen # pylint: disable=import-error +from shared import clean_eagle, clean_tester +from shared.python_like_apis import Fixture_ListLikeAPI + +# Test generator used to test the frontend <-> backend generator hooks +class PyTestGenerator: + def __init__(self): + self.nodes = [] + + def generate(self, ast): + print("Printing StubPyAST to console...") + for i, n in enumerate(ast): + if n.fields["type"] == "node": + node_str = "Node" + elif n.fields["type"] == "comment": + node_str = f"Comment - Content: {n.fields['content']}" + elif n.fields["type"] == "vector": + node_str = f"Vector - Repeat: {n.fields['repeat']}" + else: + node_str = f"Error! Unknown type {n.fields['type']}" + print(f" PyTestGenerator: Node {i}: {node_str}") + +class TestPyAST(Fixture_ListLikeAPI): + + def verify_i0(self, i): + assert i.fields["type"] == "comment" + assert i.fields["content"] == "Start!" + + def verify_i1(self, i): + assert i.fields["type"] == "vector" + assert i.fields["repeat"] == 4 + + def verify_i2(self, i): + assert i.fields["type"] == "comment" + assert i.fields["content"] == "End!" + + def boot_list_under_test(self): + origen.app.instantiate_dut("dut.eagle") + origen.tester.set_timeset("simple") + origen.tester.cc("Start!") + origen.tester.repeat(4) + origen.tester.cc("End!") + return origen.tester.ast + +def test_init_state(clean_eagle, clean_tester): + # The 'clean_tester' fixture has a number of asserts itself, + # but just in case those change unbeknownst to this method, double check the initial state here. + assert origen.tester + assert origen.tester.targets == [] + assert len(origen.tester.ast) == 0 + assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7"] + assert origen.tester.timeset is None + +def test_setting_a_timeset(clean_eagle, clean_tester): + origen.tester.set_timeset("simple") + assert isinstance(origen.tester.timeset, _origen.dut.timesets.Timeset) + assert origen.tester.timeset.name == "simple" + + origen.tester.timeset = "complex" + assert isinstance(origen.tester.timeset, _origen.dut.timesets.Timeset) + assert origen.tester.timeset.name == "complex" + +def test_resetting_the_timeset(): + assert origen.tester.timeset.name == "complex" + origen.tester.timeset = None + assert origen.tester.timeset is None + +def test_exception_on_unknown_timeset(clean_eagle, clean_tester): + with pytest.raises(OSError): + origen.tester.set_timeset("blah") + +def test_setting_timeset_with_instance(clean_eagle, clean_tester): + assert origen.tester.timeset is None + origen.tester.set_timeset(origen.dut.timesets["simple"]) + assert origen.tester.timeset.name == "simple" + +def test_setting_targets(clean_eagle, clean_tester): + assert origen.tester.targets == [] + origen.tester.target("::DummyGenerator") + assert origen.tester.targets == ["::DummyGenerator"] + +def test_resetting_targets(): + assert origen.tester.targets == ["::DummyGenerator"] + origen.tester.clear_targets() + assert origen.tester.targets == [] + +def test_exception_on_duplicate_targets(clean_eagle, clean_tester): + origen.tester.target("::DummyGenerator") + with pytest.raises(OSError): + origen.tester.target("::DummyGenerator") + +def test_exception_on_unknown_target(clean_eagle, clean_tester): + with pytest.raises(OSError): + origen.tester.target("blah") + +def test_ast_retrieval(clean_eagle, clean_tester): + assert origen.tester.ast is not None + assert isinstance(origen.tester.ast, _origen.tester.StubPyAST) + assert len(origen.tester.ast) == 0 + +class TestTesterAPI: + def test_cycle(self, clean_eagle, clean_tester): + origen.tester.set_timeset("simple") + assert len(origen.tester.ast) == 0 + assert origen.tester.ast.cycle_count == 0 + assert origen.tester.ast.vector_count == 0 + origen.tester.cycle() + assert len(origen.tester.ast) == 1 + assert origen.tester.ast[0].fields["type"] == "vector" + assert origen.tester.ast[0].fields["repeat"] == 1 + assert origen.tester.ast.cycle_count == 1 + assert origen.tester.ast.vector_count == 1 + + def test_multiple_cycles(self, clean_eagle, clean_tester): + origen.tester.set_timeset("simple") + assert len(origen.tester.ast) == 0 + assert origen.tester.ast.cycle_count == 0 + assert origen.tester.ast.vector_count == 0 + origen.tester.cycle() + origen.tester.cycle() + origen.tester.cycle() + assert len(origen.tester.ast) == 3 + assert origen.tester.ast.cycle_count == 3 + assert origen.tester.ast.vector_count == 3 + + def test_cc(self, clean_eagle, clean_tester): + origen.tester.set_timeset("simple") + assert len(origen.tester.ast) == 0 + origen.tester.cc("Hello Tester!") + assert len(origen.tester.ast) == 1 + assert origen.tester.ast[0].fields["type"] == "comment" + assert origen.tester.ast[0].fields["content"] == "Hello Tester!" + + def test_repeat(self, clean_eagle, clean_tester): + origen.tester.set_timeset("simple") + assert len(origen.tester.ast) == 0 + assert origen.tester.ast.cycle_count == 0 + assert origen.tester.ast.vector_count == 0 + origen.tester.repeat(10) + assert len(origen.tester.ast) == 1 + assert origen.tester.ast[0].fields["type"] == "vector" + assert origen.tester.ast[0].fields["repeat"] == 10 + assert origen.tester.ast.cycle_count == 10 + assert origen.tester.ast.vector_count == 1 + + def test_multiple_cycles_and_repeat(self, clean_eagle, clean_tester): + origen.tester.set_timeset("simple") + assert len(origen.tester.ast) == 0 + assert origen.tester.ast.cycle_count == 0 + assert origen.tester.ast.vector_count == 0 + origen.tester.cycle() + origen.tester.cycle(repeat=11) + origen.tester.cycle() + assert len(origen.tester.ast) == 3 + assert origen.tester.ast.cycle_count == 13 + assert origen.tester.ast.vector_count == 3 + +def test_adding_frontend_generator(clean_eagle, clean_tester): + assert "tester_test.PyTestGenerator" not in origen.tester.generators + origen.tester.register_generator(PyTestGenerator) + assert "tester_test.PyTestGenerator" in origen.tester.generators + +def test_frontend_generators_can_be_targeted(): + origen.tester.clear_targets() + assert "tester_test.PyTestGenerator" in origen.tester.generators + assert origen.tester.targets == [] + origen.tester.target("tester_test.PyTestGenerator") + assert origen.tester.targets == ["tester_test.PyTestGenerator"] + +def test_frontend_generators_can_be_targeted_as_class(): + origen.tester.clear_targets() + assert "tester_test.PyTestGenerator" in origen.tester.generators + assert origen.tester.targets == [] + origen.tester.target(PyTestGenerator) + assert origen.tester.targets == ["tester_test.PyTestGenerator"] + +def run_pattern(): + origen.tester.cc("Pattern Start!") + origen.tester.repeat(5) + origen.tester.cc("Pattern End!") + +@pytest.fixture +def tester_target_backend_dummy(): + origen.tester.target("::DummyGenerator") + +@pytest.fixture +def tester_target_frontend_dummy(): + try: + origen.tester.register_generator(PyTestGenerator) + except OSError: + # If we get an error back that shows it's already been added, that's fine. Ignore it. + pass + origen.tester.target("tester_test.PyTestGenerator") + +class TestBackendGenerator: + def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_backend_dummy): + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Printing StubAST to console...", + " ::DummyGenerator Node 0: Comment - Content: Pattern Start!", + " ::DummyGenerator Node 1: Vector - Repeat: 5", + " ::DummyGenerator Node 2: Comment - Content: Pattern End!", + "" + ]) + assert err == "" + +class TestFrontendGenerator: + def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_frontend_dummy): + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Printing StubPyAST to console...", + " PyTestGenerator: Node 0: Comment - Content: Pattern Start!", + " PyTestGenerator: Node 1: Vector - Repeat: 5", + " PyTestGenerator: Node 2: Comment - Content: Pattern End!", + "" + ]) + assert err == "" diff --git a/python/origen/__init__.py b/python/origen/__init__.py index dc63a97f..4d76972a 100644 --- a/python/origen/__init__.py +++ b/python/origen/__init__.py @@ -3,6 +3,8 @@ from pathlib import Path import importlib +from origen.tester import Tester, DummyTester + config = _origen.config() status = _origen.status() root = Path(status["root"]) @@ -11,7 +13,10 @@ app = None dut = None -tester = None +tester = Tester() +#tester = _origen.tester.PyTester("placeholder") +#tester.register_generator(DummyTester) + mode = "development" if status["is_app_present"]: @@ -38,3 +43,19 @@ def standard_context(): "dut": dut, "tester": tester, } + +# class Tester: +# def __init__(self, path): +# self.path = path +# self.db = _origen.tester.PyTester(path) + +# def instantiate_tester(path): +# t = Tester(path) +# # -- do some error checking here --- +# # ... +# tester = t +# return tester + +# Returns the dummy tester +#def instantiate_dummy_tester(): +# return instantiate_tester(origen.testers.dummy) diff --git a/python/origen/application.py b/python/origen/application.py index 6887eef9..66451416 100644 --- a/python/origen/application.py +++ b/python/origen/application.py @@ -35,6 +35,9 @@ def instantiate_dut(self, path): if not isinstance(dut, TopLevel): raise RuntimeError("The DUT object is not an instance of origen.application::TopLevel") origen.dut = dut + if origen.tester: + # Clear the tester as it may hold references to a previous DUT's backend which was just wiped out. + origen.tester.reset() return dut # Instantiate the given block and return it diff --git a/python/origen/tester.py b/python/origen/tester.py new file mode 100644 index 00000000..c21426ed --- /dev/null +++ b/python/origen/tester.py @@ -0,0 +1,26 @@ +import origen +import _origen + +class Tester(_origen.tester.PyTester): + def __init__(self): + pass + #self.db = _origen.tester.PyTester("placeholder") + #_origen.tester.PyTester.init(self, "placeholder") + + def set_timeset(self, tset): + # For simplicity, a timeset can be given as a string which is assumed to be a top-level timeset. + # Due to lazy loading though, its possible that the timesets haven't been instantiated yet, causing + # a very confusing 'no timeset found' error, yet then using 'dut.timesets' to check shows them as loaded. + # Load them here, if they haven't already. + if origen.dut and not origen.dut.timesets_loaded: + origen.dut.timesets + return _origen.tester.PyTester.set_timeset(self, tset) + + +class DummyTester: + def __init__(self): + pass + + def generate(self, ast): + for i, n in enumerate(ast.nodes): + print(f"Python Generator: Node: {i}: {n}") \ No newline at end of file diff --git a/rust/origen/meta/src/id_getters_derive.rs b/rust/origen/meta/src/id_getters_derive.rs index 864d2a57..8d6d704b 100644 --- a/rust/origen/meta/src/id_getters_derive.rs +++ b/rust/origen/meta/src/id_getters_derive.rs @@ -70,15 +70,16 @@ pub fn impl_id_getters(ast: &syn::DeriveInput) -> syn::Result { let field_container_name = format_ident!("{}", config.field_container_name); let parent_field = format_ident!("{}", config.parent_field); - let (lookup_type, error_message); + let (lookup_type, error_message, err_str); if config.getter_type == "by_index" { lookup_type = quote! { usize }; - error_message = quote! { &format!("Could not find #config.field at index {}!", identifier) }; + err_str = format!("\"Could not find {} at index {{}}!\"", config.field); } else { lookup_type = quote!{ &str }; - error_message = quote! { &format!("Could not find #config.field named {}!", identifier) }; + err_str = format!("\"Could not find {} named {{}}!\"", config.field); } - + error_message = quote! { &format!(#err_str, identifier) }; + getter_functions.extend(quote! { impl #name { pub fn #func_name (&self, parent_field_id: usize, identifier: #lookup_type) -> Option<& #retn > { diff --git a/rust/origen/src/core/mod.rs b/rust/origen/src/core/mod.rs index 40cb4eb4..1ad84f51 100644 --- a/rust/origen/src/core/mod.rs +++ b/rust/origen/src/core/mod.rs @@ -6,3 +6,4 @@ pub mod os; pub mod status; pub mod term; pub mod utility; +pub mod tester; diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs new file mode 100644 index 00000000..e8491463 --- /dev/null +++ b/rust/origen/src/core/tester.rs @@ -0,0 +1,413 @@ +use crate::error::Error; +use super::model::timesets::timeset::{Timeset}; +use indexmap::IndexMap; +use crate::testers::v93k::Generator as V93KGen; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Generators { + DummyGenerator, + DummyGeneratorWithInterceptors, + V93kSt7, + //J750, + //Uflex, + External(String), +} + +impl Generators { + pub fn from_str(s: &str) -> Option { + match s { + "::DummyGenerator" => Some(Self::DummyGenerator), + "::DummyGeneratorWithInterceptors" => Some(Self::DummyGeneratorWithInterceptors), + "::V93K::ST7" => Some(Self::V93kSt7), + _ => None + } + } + + pub fn to_string(&self) -> String { + match self { + Self::DummyGenerator => "::DummyGenerator".to_string(), + Self::DummyGeneratorWithInterceptors => "::DummyGeneratorWithInterceptors".to_string(), + Self::V93kSt7 => "::V93K::ST7".to_string(), + Self::External(g) => g.clone(), + } + } + + pub fn to_vector_strings(&self) -> Vec { + vec!("::DummyGenerator".to_string(), "::DummyGeneratorWithInterceptors".to_string(), "::V93K::ST7".to_string()) + } +} + +#[derive(Debug)] +pub struct ExternalGenerator { + name: String, + source: String, + generator: Box, +} + +#[derive(Debug)] +pub enum StubNodes { + Comment { + content: String, + meta: Vec, + }, + Vector { + //timeset: String, + timeset_id: usize, + repeat: usize, + meta: Vec, + }, + Node { + meta: Vec, + }, +} + +#[derive(Debug)] +pub struct StubAST { + pub nodes: Vec, + vector_count: usize, + cycle_count: usize, +} + +impl StubAST { + pub fn new() -> Self { + Self { + nodes: vec!(), + vector_count: 0, + cycle_count: 0, + } + } + + pub fn reset(&mut self) -> () { + self.nodes.clear(); + self.vector_count = 0; + self.cycle_count = 0; + } + + pub fn push_comment(&mut self, comment: &str) -> Result<(), Error> { + //self.nodes.push(comment.to_string()); + self.nodes.push(StubNodes::Comment { + content: comment.to_string(), + meta: vec!(), + }); + Ok(()) + } + + pub fn push_vector(&mut self, timeset_id: usize, repeat: usize) -> Result<(), Error> { + //self.nodes.push(format!("Vector - Timeset ID: {}, Repeat: {}", timeset_id, repeat).to_string()); + self.nodes.push(StubNodes::Vector { + timeset_id: timeset_id, + repeat: repeat, + meta: vec!(), + }); + self.vector_count += 1; + self.cycle_count += repeat; + Ok(()) + } + + pub fn cycle_count(&self) -> usize { + self.cycle_count + } + + pub fn vector_count(&self) -> usize { + self.vector_count + } + + pub fn len(&self) -> usize { + self.nodes.len() + } + + // pub fn get_node(&self, idx) { + + // } +} + +#[derive(Debug)] +pub struct Tester { + /// The current timeset ID, if its set. + /// This is the direct ID to the timeset object. + /// The name and model ID can be found on this object. + current_timeset_id: Option, + + /// Stubbed AST. Replace this with something else when it becomes available. + ast: StubAST, + + external_generators: IndexMap, + pub target_generators: Vec, +} + +impl Tester { + pub fn new() -> Self { + Tester { + current_timeset_id: Option::None, + ast: StubAST::new(), + external_generators: IndexMap::new(), + target_generators: vec!(), + } + } + + pub fn _current_timeset_id(&self) -> Result { + match self.current_timeset_id { + Some(t_id) => Ok(t_id), + None => Err(Error::new(&format!("No timeset has been set!"))) + } + } + + pub fn reset(&mut self) -> Result<(), Error> { + self.ast.reset(); + self.current_timeset_id = Option::None; + self.target_generators.clear(); + self.external_generators.clear(); + Ok(()) + } + + pub fn register_external_generator(&mut self, generator: &str) -> Result<(), Error> { + self.external_generators.insert(generator.to_string(), Generators::External(generator.to_string())); + Ok(()) + } + + pub fn get_timeset(&self, dut: &super::dut::Dut) -> Option { + if let Some(t_id) = self.current_timeset_id { + Some(dut.timesets[t_id].clone()) + } else { + Option::None + } + } + + pub fn _get_timeset(&self, dut: &super::dut::Dut) -> Result { + if let Some(t_id) = self.current_timeset_id { + Ok(dut.timesets[t_id].clone()) + } else { + Err(Error::new(&format!("No timeset has been set!"))) + } + } + + pub fn set_timeset(&mut self, dut: &super::dut::Dut, model_id: usize, timeset_name: &str) -> Result<(), Error> { + self.current_timeset_id = Some(dut._get_timeset(model_id, timeset_name)?.id); + self.issue_callbacks("set_timeset")?; + Ok(()) + } + + pub fn clear_timeset(&mut self) -> Result<(), Error> { + self.current_timeset_id = Option::None; + self.issue_callbacks("clear_timeset")?; + Ok(()) + } + + pub fn issue_callbacks(&mut self, func: &str) -> Result<(), Error> { + for g in self.target_generators.iter() { + match g { + Generators::DummyGeneratorWithInterceptors => { + let g_ = DummyGeneratorWithInterceptors{}; + if func == "cc" { + g_.cc(&mut self.ast)?; + } else if func == "cycle" { + g_.cycle(&mut self.ast)?; + }; + }, + _ => {} + } + } + Ok(()) + } + + pub fn cc(&mut self, comment: &str) -> Result<(), Error> { + self.ast.push_comment(comment)?; + self.issue_callbacks("cc")?; + Ok(()) + } + + pub fn cycle(&mut self, repeat: Option) -> Result<(), Error>{ + self.ast.push_vector(self._current_timeset_id()?, repeat.unwrap_or(1)); + self.issue_callbacks("cycle")?; + Ok(()) + } + + pub fn generate(&mut self) -> Result { + let mut stat = GenerateStatus::new(); + let num_targets; + { + num_targets = self.target_generators.len(); + } + //for (i, g) in self.target_generators.iter().enumerate() { + for i in 0..num_targets { + let stat_ = self.generate_target_at(i)?; + stat.completed.extend(stat_.completed); + stat.external.extend(stat_.external); + // match g { + // Generators::DummyGenerator => { + // DummyGenerator{}.generate(&self.ast)?; + // stat.completed.push(Generators::DummyGenerator.to_string()) + // }, + // Generators::DummyGeneratorWithInterceptors => { + // DummyGeneratorWithInterceptors{}.generate(&self.ast)?; + // stat.completed.push(Generators::DummyGeneratorWithInterceptors.to_string()) + // } + // Generators::V93K_ST7 => { + // V93KGen{}.generate(&self.ast)?; + // stat.completed.push(Generators::V93K_ST7.to_string()) + // } + // Generators::External(gen) => { + // stat.external.push(gen.to_string()); + // } + // } + } + Ok(stat) + } + + /// Generates the output for the target at index i. + /// Allows the frontend to call generators in a loop. + pub fn generate_target_at(&mut self, idx: usize) -> Result { + let mut stat = GenerateStatus::new(); + let g = &self.target_generators[idx]; + match g { + Generators::DummyGenerator => { + DummyGenerator{}.generate(&self.ast)?; + stat.completed.push(Generators::DummyGenerator.to_string()) + }, + Generators::DummyGeneratorWithInterceptors => { + DummyGeneratorWithInterceptors{}.generate(&self.ast)?; + stat.completed.push(Generators::DummyGeneratorWithInterceptors.to_string()) + } + Generators::V93kSt7 => { + V93KGen{}.generate(&self.ast)?; + stat.completed.push(Generators::V93kSt7.to_string()) + } + Generators::External(gen) => { + stat.external.push(gen.to_string()); + } + } + Ok(stat) + } + + pub fn target(&mut self, generator: &str) -> Result<(), Error> { + let g; + if let Some(_g) = Generators::from_str(generator) { + g = _g; + } else if let Some(_g) = self.external_generators.get(generator) { + g = (*_g).clone(); + } else { + return Err(Error::new(&format!("Could not find generator '{}'!", generator))); + } + + if self.target_generators.contains(&g) { + Err(Error::new(&format!("Generator {} has already been targeted!", generator))) + } else { + self.target_generators.push(g); + Ok(()) + } + } + + pub fn targets(&self) -> &Vec { + &self.target_generators + } + + pub fn targets_as_strs(&self) -> Vec { + self.target_generators.iter().map( |g| g.to_string()).collect() + } + + pub fn clear_targets(&mut self) -> Result<(), Error> { + self.target_generators.clear(); + Ok(()) + } + + pub fn get_ast(&self) -> &StubAST { + &self.ast + } + + pub fn get_mut_ast(&mut self) -> &mut StubAST { + &mut self.ast + } + + pub fn generators(&self) -> Vec { + let mut gens = Generators::DummyGenerator.to_vector_strings(); + //let temp = self.external_generators.iter().map ( |n| n).collect::>(); + gens.extend(self.external_generators.iter().map(|(n, _)| n.clone()).collect::>()); + gens + } +} + +pub struct GenerateStatus { + pub completed: Vec, + pub external: Vec, +} + +impl GenerateStatus { + pub fn new() -> Self { + Self { + completed: vec!(), + external: vec!(), + } + } +} + +struct Generator { + name: String, + path: String, + +} + +struct DummyGenerator {} + +impl DummyGenerator { + + /// A dummy generator which simply prints everything to the screen. + pub fn generate(&self, ast: &StubAST) -> Result<(), Error> { + println!("Printing StubAST to console..."); + for (i, n) in ast.nodes.iter().enumerate() { + match n { + StubNodes::Comment {content, meta} => println!(" ::DummyGenerator Node {}: Comment - Content: {}", i, content), + StubNodes::Vector {timeset_id, repeat, meta} => println!(" ::DummyGenerator Node {}: Vector - Repeat: {}", i, repeat), + StubNodes::Node {meta} => println!(" ::DummyGenerator Node {}: Node", i), + } + } + Ok(()) + } +} + +struct DummyGeneratorWithInterceptors {} + +impl DummyGeneratorWithInterceptors { + + /// A dummy generator which simply prints everything to the screen. + pub fn generate(&self, ast: &StubAST) -> Result<(), Error> { + println!("Printing StubAST to console..."); + for (i, n) in ast.nodes.iter().enumerate() { + match n { + StubNodes::Comment {content, meta} => println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", i, content), + StubNodes::Vector {timeset_id, repeat, meta} => println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}", i, repeat), + StubNodes::Node {meta} => println!(" ::DummyGeneratorWithInterceptors Node {}: Node", i), + } + } + Ok(()) + } + + pub fn cycle(&self, ast: &mut StubAST) -> Result<(), Error> { + let n = ast.nodes.last_mut().unwrap(); + match n { + StubNodes::Vector {timeset_id, repeat, meta} => { + println!("Vector intercepted by DummyGeneratorWithInterceptors!"); + Ok(()) + }, + _ => Err(Error::new(&format!("Error Intercepting Vector! Expected vector node!"))) + } + } + + pub fn cc(&self, ast: &mut StubAST) -> Result<(), Error> { + let n = ast.nodes.last().unwrap(); + let n_; + match n { + StubNodes::Comment {content, meta} => { + println!("Comment intercepted by DummyGeneratorWithInterceptors!"); + n_ = StubNodes::Comment { + content: String::from(format!(" Comment intercepted by DummyGeneratorWithInterceptors! {}", content.clone())), + meta: meta.clone(), + }; + }, + _ => return Err(Error::new(&format!("Error Intercepting Comment! Expected comment node!"))) + } + drop(n); + let i = ast.len() - 1; + ast.nodes[i] = n_; + Ok(()) + } +} diff --git a/rust/origen/src/lib.rs b/rust/origen/src/lib.rs index 3635b498..cda726de 100644 --- a/rust/origen/src/lib.rs +++ b/rust/origen/src/lib.rs @@ -5,12 +5,14 @@ extern crate serde; extern crate meta; pub mod core; +pub mod testers; pub mod error; pub use error::Error; use self::core::application::config::Config as AppConfig; use self::core::config::Config as OrigenConfig; pub use self::core::dut::Dut; +pub use self::core::tester::Tester; use self::core::status::Status; use self::core::utility::logger::Logger; use std::sync::{Mutex, MutexGuard}; @@ -34,6 +36,8 @@ lazy_static! { /// timing, etc. and responsible for maintaining the current state of the DUT (regs, pins, /// etc.) pub static ref DUT: Mutex = Mutex::new(Dut::new("placeholder")); + /// The global tester model. + pub static ref TESTER: Mutex = Mutex::new(Tester::new()); } // Use of a mod or pub mod is not actually necessary. @@ -58,6 +62,10 @@ pub fn dut() -> MutexGuard<'static, Dut> { DUT.lock().unwrap() } +pub fn tester() -> MutexGuard<'static, Tester> { + TESTER.lock().unwrap() +} + /// Sanitizes the given mode string and returns it, but will exit the process if it is invalid pub fn clean_mode(name: &str) -> String { let mut matches: Vec = Vec::new(); diff --git a/rust/origen/src/testers/igxl/.gitkeep b/rust/origen/src/testers/igxl/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/rust/origen/src/testers/mod.rs b/rust/origen/src/testers/mod.rs new file mode 100644 index 00000000..d3f198d2 --- /dev/null +++ b/rust/origen/src/testers/mod.rs @@ -0,0 +1 @@ +pub mod v93k; \ No newline at end of file diff --git a/rust/origen/src/testers/v93k/mod.rs b/rust/origen/src/testers/v93k/mod.rs new file mode 100644 index 00000000..b0cdc22f --- /dev/null +++ b/rust/origen/src/testers/v93k/mod.rs @@ -0,0 +1,18 @@ +use crate::core::tester::{StubAST, StubNodes}; +use crate::error::Error; + +pub struct Generator {} + +impl Generator { + /// An extremely elementary generator for the 93. Won't actually generate anything of use yet. + pub fn generate(&self, ast: &StubAST) -> Result<(), Error> { + for (i, n) in ast.nodes.iter().enumerate() { + match n { + StubNodes::Comment {content, meta} => println!("# {}", content), + StubNodes::Vector {timeset_id, repeat, meta} => println!("R{} ;", repeat), + StubNodes::Node {meta} => return Err(Error::new(&format!("Pure meta nodes are not supported by the V93K yet!"))), + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/rust/pyapi/Cargo.toml b/rust/pyapi/Cargo.toml index 3d0e71d4..c0840a6b 100644 --- a/rust/pyapi/Cargo.toml +++ b/rust/pyapi/Cargo.toml @@ -22,4 +22,4 @@ indexmap = "1.3.0" [dependencies.pyo3] version = "0.8.2" -features = ["extension-module", "num-bigint"] +features = ["extension-module", "num-bigint", "unsound-subclass"] diff --git a/rust/pyapi/src/lib.rs b/rust/pyapi/src/lib.rs index ff0ab981..fd987fbb 100644 --- a/rust/pyapi/src/lib.rs +++ b/rust/pyapi/src/lib.rs @@ -3,7 +3,9 @@ mod logger; mod meta; mod pins; mod registers; +#[macro_use] mod timesets; +mod tester; use origen::{APPLICATION_CONFIG, ORIGEN_CONFIG, STATUS}; use pyo3::prelude::*; @@ -12,6 +14,7 @@ use pyo3::{wrap_pyfunction, wrap_pymodule}; // Imported pyapi modules use dut::PyInit_dut; +use tester::PyInit_tester; use logger::PyInit_logger; #[pymodule] @@ -25,6 +28,7 @@ fn _origen(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(logger))?; m.add_wrapped(wrap_pymodule!(dut))?; + m.add_wrapped(wrap_pymodule!(tester))?; Ok(()) } diff --git a/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs b/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs index e0421b81..ee4a410c 100644 --- a/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs +++ b/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs @@ -2,7 +2,6 @@ use origen::DUT; use pyo3::prelude::*; -use origen::error::Error; use indexmap::map::IndexMap; // dut: &std::sync::MutexGuard @@ -43,7 +42,7 @@ pub trait DictLikeAPI { fn model_id(&self) -> usize; fn lookup_key(&self) -> &str; fn lookup_table(&self, dut: &std::sync::MutexGuard) -> IndexMap; - fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> Result; + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult; // #[proc_macro] // fn lookup_code(input: TokenStream) -> TokenStream { diff --git a/rust/pyapi/src/meta/py_like_apis/list_like_api.rs b/rust/pyapi/src/meta/py_like_apis/list_like_api.rs index 4c0befbe..5a88bc82 100644 --- a/rust/pyapi/src/meta/py_like_apis/list_like_api.rs +++ b/rust/pyapi/src/meta/py_like_apis/list_like_api.rs @@ -1,11 +1,10 @@ use origen::DUT; use pyo3::prelude::*; -use origen::error::Error; use pyo3::types::{PyAny, PySlice}; pub trait ListLikeAPI { fn item_ids(&self, dut: &std::sync::MutexGuard) -> Vec; - fn new_pyitem(&self, py: Python, idx: usize) -> Result; + fn new_pyitem(&self, py: Python, idx: usize) -> PyResult; fn __iter__(&self) -> PyResult; fn __getitem__(&self, idx: &PyAny) -> PyResult { diff --git a/rust/pyapi/src/tester.rs b/rust/pyapi/src/tester.rs new file mode 100644 index 00000000..7bc8440c --- /dev/null +++ b/rust/pyapi/src/tester.rs @@ -0,0 +1,418 @@ +use pyo3::prelude::*; +use pyo3::types::{PyAny, PyDict, PyTuple}; +use super::timesets::timeset::{Timeset}; +use std::collections::HashMap; +use origen::error::Error; +use super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; +use origen::core::tester::{StubNodes, Generators}; +//use pyo3::type_object::PyTypeObject; + +#[pymodule] +pub fn tester(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) +} + +#[pyclass(subclass)] +#[derive(Debug)] +pub struct PyTester { + python_generators: HashMap, + metadata: Vec, + //py_ast: PyObject, +} + +#[pymethods] +impl PyTester { + #[new] + fn new(obj: &PyRawObject) { + origen::tester().reset().unwrap(); + obj.init({ PyTester { + python_generators: HashMap::new(), + metadata: vec!(), + // py_ast: { + // let gil = Python::acquire_gil(); + // let py = gil.python(); + // Py::new(py, StubPyAST {}).unwrap().to_object(py) + // } + } + }); + } + + pub fn push_metadata(&mut self, item: &PyAny) -> usize { + let gil = Python::acquire_gil(); + let py = gil.python(); + + self.metadata.push(item.to_object(py)); + self.metadata.len() - 1 + } + + pub fn override_metadata_at(&mut self, idx: usize, item: &PyAny) -> PyResult<()> { + let gil = Python::acquire_gil(); + let py = gil.python(); + if self.metadata.len() > idx { + self.metadata[idx] = item.to_object(py); + Ok(()) + } else { + Err(PyErr::from(Error::new(&format!( + "Overriding metadata at {} exceeds the size of the current metadata vector!", + idx + )))) + } + } + + pub fn get_metadata(&self, idx: usize) -> PyResult<&PyObject> { + Ok(&self.metadata[idx]) + } + + fn reset(slf: PyRef) -> PyResult { + origen::tester().reset()?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + + #[getter] + fn get_timeset(&self) -> PyResult { + let tester = origen::tester(); + let dut = origen::dut(); + let gil = Python::acquire_gil(); + let py = gil.python(); + if let Some(t) = tester.get_timeset(&dut) { + Ok(Py::new(py, Timeset { + name: t.name.clone(), + model_id: t.model_id + }).unwrap().to_object(py)) + } else { + Ok(py.None()) + } + } + + #[setter] + fn timeset(&self, timeset: &PyAny) -> PyResult { + let (model_id, timeset_name); + + // If the timeset is a string, assume its a timeset name on the DUT. + // If not, it should be either None, to clear the timeset, + // or a timeset object, in which case we'll look up the name and model ID and go from there. + if let Ok(_timeset) = timeset.extract::() { + model_id = 0; + timeset_name = _timeset; + } else { + if timeset.get_type().name().to_string() == "NoneType" { + { + let mut tester = origen::TESTER.lock().unwrap(); + tester.clear_timeset()?; + } + return self.get_timeset(); + //return type_error!(format!("Test! (class '{}')", timeset.get_type().name())); + } else if timeset.get_type().name().to_string() == "Timeset" { + let gil = Python::acquire_gil(); + let py = gil.python(); + let obj = timeset.to_object(py); + //let m = obj.getattr(py, "__module__")?.extract::(py)?; + + //if m == "_origen.dut.timesets" { + model_id = obj.getattr(py, "__origen__model_id__")?.extract::(py)?; + timeset_name = obj.getattr(py, "name")?.extract::(py)?; + //} else { + // return type_error!(format!("Could not interpret 'timeset' argument as _origen.dut.timesets.Timeset object! It appears to be of type 'Timeset', but of module '{}'", m)); + //} + } else { + //let obj = timeset.to_object(); // timeset.get_type().name().to_string() == "_origen.dut.timesets.Timeset" + //if obj.call0() + //let t = pyo3::type_object::PyTypeObject::type_object(timeset); + //timeset.get_type().is_instance(); + return type_error!(format!("Could not interpret 'timeset' argument as String or _origen.dut.timesets.Timeset object! (class '{}')", timeset.get_type().name())); + } + } + + { + let mut tester = origen::TESTER.lock().unwrap(); + let dut = origen::DUT.lock().unwrap(); + tester.set_timeset(&dut, model_id, ×et_name)?; + } + self.get_timeset() + // let model = dut.get_model(model_id).unwrap(); + // let gil = Python::acquire_gil(); + // let py = gil.python(); + // pytimeset!(py, model, model_id, ×et_name) + } + + fn set_timeset(&self, timeset: &PyAny) -> PyResult { + self.timeset(timeset) + } + + fn cc(slf: PyRef, comment: &str) -> PyResult { + let mut tester = origen::tester(); + tester.cc(&comment)?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + + /// Expecting more arguments/options to eventually be added here. + #[args(kwargs="**")] + fn cycle(slf: PyRef, kwargs: Option<&PyDict>) -> PyResult { + let mut tester = origen::tester(); + let mut repeat = None; + if let Some(_kwargs) = kwargs { + if let Some(_kwarg) = _kwargs.get_item("repeat") { + repeat = Some(_kwarg.extract::()?); + } + } + tester.cycle(repeat)?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + + fn repeat(slf: PyRef, count: usize) -> PyResult { + let gil = Python::acquire_gil(); + let py = gil.python(); + let kwargs = PyDict::new(py); + kwargs.set_item("repeat", count)?; + Self::cycle(slf, Some(&kwargs)) + } + + fn register_generator(&mut self, g: &PyAny) -> PyResult<()> { + let mut tester = origen::tester(); + //let name = generator.get_type().name().to_string(); + //let klass = g.downcast_ref::()?; + //let name = klass.name().to_string(); //g.name().to_string(); + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = g.to_object(py); + let mut n = obj.getattr(py, "__module__")?.extract::(py)?; + n.push_str(&format!(".{}", obj.getattr(py, "__qualname__")?.extract::(py)?)); + //let name = klass_name.extract::(py)?; + + tester.register_external_generator(&n)?; + //let t = g.downcast_ref::()?; + self.python_generators.insert(n, obj); + Ok(()) + } + + #[args(generators="*")] + fn target(&self, generators: &PyTuple) -> PyResult> { + if generators.len() > 0 { + let mut tester = origen::tester(); + for g in generators.iter() { + // Accept either a string name or the actual class of the generator + if let Ok(name) = g.extract::() { + tester.target(&name)?; + } else { + // let gil = Python::acquire_gil(); + // let py = gil.python(); + // let klass = g.to_object(py); + // let klass = g.downcast_ref::()?; + // let name = klass.name().to_string(); //g.name().to_string(); + // tester.target(&name)?; + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = g.to_object(py); + let mut n = obj.getattr(py, "__module__")?.extract::(py)?; + n.push_str(&format!(".{}", obj.getattr(py, "__qualname__")?.extract::(py)?)); + tester.target(&n)?; + } + } + } + self.targets() + } + + #[getter] + fn targets(&self) -> PyResult> { + let tester = origen::tester(); + Ok(tester.targets_as_strs().clone()) + } + + fn clear_targets(slf: PyRef) -> PyResult { + let mut tester = origen::tester(); + tester.clear_targets()?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + + // fn active_generator(&self) -> PyResult { + // // ... + // } + + fn generate(&self) -> PyResult<()> { + //let stat; + let mut targets: Vec = vec!(); + { + let mut tester = origen::tester(); + //stat = tester.generate()?; + targets = tester.targets().clone(); + } + //for g in stat.external { + for (i, t) in targets.iter().enumerate() { + match t { + Generators::External(g) => { + // External generators which the backend can't generate itself. Need to generate them here. + match self.python_generators.get(g) { + Some(gen) => { + // The generator here is a PyObject - a handle on the class itself. + // Instantiate it and call its generate method with the AST. + let gil = Python::acquire_gil(); + let py = gil.python(); + //let klass = gen.to_object(py); + //let obj = gen.call_method0(py, "__new__", )?; + //gen.call_method0(py, "__init__")?; + let inst = gen.call0(py)?; + let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); + inst.call_method1(py, "generate", args)?; + }, + None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), + } + }, + _ => { + let mut tester = origen::tester(); + tester.generate_target_at(i)?; + } + } + } + Ok(()) + } + + #[getter] + fn generators(&self) -> PyResult> { + let tester = origen::tester(); + Ok(tester.generators()) + } + + #[getter] + fn ast(&self) -> PyResult { + //Ok(self.py_ast) + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(Py::new(py, StubPyAST {})?.to_object(py)) + } +} + +impl PyTester { + + // /// Retrieves a built timeset object from the Origen backend. + // fn origen_timeset(&self) -> Timeset { + // // ... + // } + + // /// Retrieves a built timeset object from the PyAPI. + // fn pyorigen_timeset(&self) -> Timeset { + // // ... + // } +} + +#[pyclass] +#[derive(Debug, Clone)] +struct StubPyAST {} + +#[pymethods] +impl StubPyAST { + #[getter] + fn cycle_count(&self) -> PyResult { + let tester = origen::tester(); + let ast = tester.get_ast(); + + Ok(ast.cycle_count()) + } + + #[getter] + fn vector_count(&self) -> PyResult { + let tester = origen::tester(); + let ast = tester.get_ast(); + + Ok(ast.vector_count()) + } +} + +impl ListLikeAPI for StubPyAST { + fn item_ids(&self, _dut: &std::sync::MutexGuard) -> Vec { + // Todo: Turns this into a macro so we don't need the DUT. + let tester = origen::tester(); + let ast = tester.get_ast(); + + // The items ids won't actually be used here since this is structured differently than stuff on the DUT. + // For prototyping purposes, and to find opportunities for improvement, just hammering in screws here. + // All we really need from this is a vector that's the same size as the number of nodes in the AST. + let mut dummy = vec!(); + dummy.resize_with(ast.len(), || { 0 }); + dummy + } + + fn new_pyitem(&self, py: Python, idx: usize) -> PyResult { + let tester = origen::tester(); + let ast = tester.get_ast(); + let node = &ast.nodes[idx]; + let dict = PyDict::new(py); + match node { + StubNodes::Comment {content, meta} => { + dict.set_item("type", "comment")?; + dict.set_item("content", content)?; + }, + StubNodes::Vector {timeset_id, repeat, meta} => { + dict.set_item("type", "vector")?; + dict.set_item("repeat", repeat)?; + }, + StubNodes::Node {meta} => { + dict.set_item("type", "node")?; + }, + } + let py_node = Py::new(py, PyNode::new(idx, dict.to_object(py))).unwrap(); + Ok(py_node.to_object(py)) + } + + fn __iter__(&self) -> PyResult { + Ok(ListLikeIter {parent: Box::new((*self).clone()), i: 0}) + } +} + +#[pyproto] +impl pyo3::class::mapping::PyMappingProtocol for StubPyAST { + fn __getitem__(&self, idx: &PyAny) -> PyResult { + ListLikeAPI::__getitem__(self, idx) + } + + fn __len__(&self) -> PyResult { + ListLikeAPI::__len__(self) + } +} + +#[pyproto] +impl pyo3::class::iter::PyIterProtocol for StubPyAST { + fn __iter__(slf: PyRefMut) -> PyResult { + ListLikeAPI::__iter__(&*slf) + } +} + +#[pyclass] +#[derive(Debug)] +pub struct PyNode { + pub fields: PyObject, + pub idx: usize, +} + +#[pymethods] +impl PyNode { + #[getter] + fn fields(&self) -> PyResult<&PyObject> { + Ok(&self.fields) + } +} + +impl PyNode { + pub fn new(idx: usize, dict: PyObject) -> Self { + Self { + fields: dict, + idx: idx, + } + } +} diff --git a/rust/pyapi/src/timesets.rs b/rust/pyapi/src/timesets.rs index 225e65fc..8f021381 100644 --- a/rust/pyapi/src/timesets.rs +++ b/rust/pyapi/src/timesets.rs @@ -13,9 +13,9 @@ macro_rules! type_error { } #[macro_use] -mod timeset_container; +pub mod timeset_container; #[macro_use] -mod timeset; +pub mod timeset; use timeset::{Timeset, Wavetable, WaveGroup, Wave, Event}; use timeset_container::{TimesetContainer, WavetableContainer, WaveGroupContainer, WaveContainer, EventContainer}; diff --git a/rust/pyapi/src/timesets/timeset.rs b/rust/pyapi/src/timesets/timeset.rs index f6c90d66..eb3a1932 100644 --- a/rust/pyapi/src/timesets/timeset.rs +++ b/rust/pyapi/src/timesets/timeset.rs @@ -127,6 +127,12 @@ impl Timeset { Ok(timeset.unwrap().name.clone()) } + #[allow(non_snake_case)] + #[getter] + fn get___origen__model_id__(&self) -> PyResult { + Ok(self.model_id) + } + #[getter] fn get_period(&self) -> PyResult { let dut = DUT.lock().unwrap(); diff --git a/rust/pyapi/src/timesets/timeset_container.rs b/rust/pyapi/src/timesets/timeset_container.rs index 779f3c48..69d0e8ae 100644 --- a/rust/pyapi/src/timesets/timeset_container.rs +++ b/rust/pyapi/src/timesets/timeset_container.rs @@ -1,6 +1,5 @@ use pyo3::prelude::*; use pyo3::class::mapping::*; -use origen::error::Error; use super::super::meta::py_like_apis::dict_like_api::{DictLikeAPI, DictLikeIter}; use super::super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; use indexmap::map::IndexMap; @@ -51,7 +50,7 @@ impl DictLikeAPI for TimesetContainer { self.model_id } - fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> Result { + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { Ok(Py::new(py, super::timeset::Timeset::new(name, model_id)).unwrap().to_object(py)) } } @@ -121,7 +120,7 @@ impl DictLikeAPI for WavetableContainer { self.model_id } - fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> Result { + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { Ok(Py::new(py, super::timeset::Wavetable::new(model_id, self.timeset_id, name)).unwrap().to_object(py)) } } @@ -192,7 +191,7 @@ impl DictLikeAPI for WaveGroupContainer { self.model_id } - fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> Result { + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { Ok(Py::new(py, super::timeset::WaveGroup::new(model_id, self.timeset_id, self.wavetable_id, name)).unwrap().to_object(py)) } } @@ -277,7 +276,7 @@ impl DictLikeAPI for WaveContainer { self.model_id } - fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> Result { + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { Ok(Py::new(py, super::timeset::Wave::new(model_id, self.timeset_id, self.wavetable_id, self.wave_group_id, name)).unwrap().to_object(py)) } } @@ -328,7 +327,7 @@ impl ListLikeAPI for EventContainer { dut.waves[self.wave_id].events.clone() } - fn new_pyitem(&self, py: Python, idx: usize) -> Result { + fn new_pyitem(&self, py: Python, idx: usize) -> PyResult { Ok(Py::new(py, super::timeset::Event::new(self.model_id, self.timeset_id, self.wavetable_id, self.wave_group_id, self.wave_id, &self.wave_name, idx)).unwrap().to_object(py)) } From c730b473c4206adcce76747ed06f917a7405cdd3 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Wed, 12 Feb 2020 08:38:45 -0600 Subject: [PATCH 02/18] Various updates for tester prototype. Updated list/dict-like APIs to be less stingy with the DUT lock. --- example/tests/tester_test.py | 84 ++++- rust/origen/src/core/tester.rs | 161 ++++----- rust/origen/src/testers/mod.rs | 3 +- rust/origen/src/testers/simulator/mod.rs | 32 ++ rust/origen/src/testers/v93k/mod.rs | 14 +- .../src/meta/py_like_apis/dict_like_api.rs | 55 +-- .../src/meta/py_like_apis/list_like_api.rs | 16 +- rust/pyapi/src/tester.rs | 337 +++++++++++++----- 8 files changed, 491 insertions(+), 211 deletions(-) create mode 100644 rust/origen/src/testers/simulator/mod.rs diff --git a/example/tests/tester_test.py b/example/tests/tester_test.py index 879285a0..0f2f5ce5 100644 --- a/example/tests/tester_test.py +++ b/example/tests/tester_test.py @@ -21,6 +21,38 @@ def generate(self, ast): node_str = f"Error! Unknown type {n.fields['type']}" print(f" PyTestGenerator: Node {i}: {node_str}") +# Test generator used to test the frontend <-> backend interceptor hooks +class PyTestGeneratorWithInterceptor: + def generate(self, ast): + print("Printing StubPyAST to console...") + for i, n in enumerate(ast): + if n.fields["type"] == "node": + node_str = "Node" + elif n.fields["type"] == "comment": + node_str = f"Comment - Content: {n.fields['content']}" + elif n.fields["type"] == "vector": + node_str = f"Vector - Repeat: {n.fields['repeat']}" + else: + node_str = f"Error! Unknown type {n.fields['type']}" + print(f" PyTestGeneratorWithInterceptor: Node {i}: {node_str}") + + def cc(self, ast): + ast[-1].set('content', f"Intercepted By PyTestGeneratorWithInterceptor: {ast[-1].fields['content']}") + +# Test generator which ignores everything and uses the meta interface only. +# Not overly practical, but this should work nonetheless. +class PyTestMetaGenerator: + def generate(self, ast): + print("Printing StubPyAST to console...") + for i, n in enumerate(ast): + print(f" PyTestMetaGenerator: {i}: {n.get_metadata('meta_gen_str')}") + + def cycle(self, ast): + ast[-1].add_metadata('meta_gen_str', f"Meta Cycle: {ast[-1].fields['repeat']}") + + def cc(self, ast): + ast[-1].add_metadata('meta_gen_str', f"Meta CC: {ast[-1].fields['content']}") + class TestPyAST(Fixture_ListLikeAPI): def verify_i0(self, i): @@ -202,11 +234,29 @@ def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_backend assert out == "\n".join([ "Printing StubAST to console...", " ::DummyGenerator Node 0: Comment - Content: Pattern Start!", - " ::DummyGenerator Node 1: Vector - Repeat: 5", + " ::DummyGenerator Node 1: Vector - Repeat: 5, Timeset: 'simple'", " ::DummyGenerator Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" + + def test_interceptors_on_backend(self, capfd, clean_eagle, clean_tester): + origen.tester.target("::DummyGeneratorWithInterceptors") + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Comment intercepted by DummyGeneratorWithInterceptors!", + "Vector intercepted by DummyGeneratorWithInterceptors!", + "Comment intercepted by DummyGeneratorWithInterceptors!", + "Printing StubAST to console...", + " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Pattern Start!", + " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Pattern End!", + "" + ]) + assert err == "" class TestFrontendGenerator: def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_frontend_dummy): @@ -222,3 +272,35 @@ def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_fronten "" ]) assert err == "" + + def test_generator_with_interceptors(self, capfd, clean_eagle, clean_tester): + origen.tester.register_generator(PyTestGeneratorWithInterceptor) + origen.tester.target(PyTestGeneratorWithInterceptor) + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Printing StubPyAST to console...", + " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Pattern Start!", + " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", + " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Pattern End!", + "" + ]) + assert err == "" + + def test_meta_generator(self, capfd, clean_eagle, clean_tester): + origen.tester.register_generator(PyTestMetaGenerator) + origen.tester.target(PyTestMetaGenerator) + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Printing StubPyAST to console...", + " PyTestMetaGenerator: 0: Meta CC: Pattern Start!", + " PyTestMetaGenerator: 1: Meta Cycle: 5", + " PyTestMetaGenerator: 2: Meta CC: Pattern End!", + "" + ]) + assert err == "" diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs index e8491463..20f2f222 100644 --- a/rust/origen/src/core/tester.rs +++ b/rust/origen/src/core/tester.rs @@ -2,6 +2,7 @@ use crate::error::Error; use super::model::timesets::timeset::{Timeset}; use indexmap::IndexMap; use crate::testers::v93k::Generator as V93KGen; +use crate::core::dut::{Dut}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Generators { @@ -48,19 +49,59 @@ pub struct ExternalGenerator { pub enum StubNodes { Comment { content: String, - meta: Vec, + meta: IndexMap, }, Vector { //timeset: String, timeset_id: usize, repeat: usize, - meta: Vec, + meta: IndexMap, }, Node { - meta: Vec, + meta: IndexMap, }, } +impl StubNodes { + pub fn add_metadata_id(&mut self, identifier: &str, id: usize) -> Result<(), Error> { + match self { + Self::Comment {content: _, meta} => { + meta.insert(identifier.to_string(), id); + }, + Self::Vector {timeset_id: _, repeat: _, meta} => { + meta.insert(identifier.to_string(), id); + }, + Self::Node {meta} => { + meta.insert(identifier.to_string(), id); + } + } + Ok(()) + } + + pub fn get_metadata_id(&self, identifier: &str) -> Option { + match self { + Self::Comment {content: _, meta} => { + match meta.get(identifier) { + Some(id) => Some(*id), + None => None, + } + }, + Self::Vector {timeset_id: _, repeat: _, meta} => { + match meta.get(identifier) { + Some(id) => Some(*id), + None => None, + } + }, + Self::Node {meta} => { + match meta.get(identifier) { + Some(id) => Some(*id), + None => None, + } + } + } + } +} + #[derive(Debug)] pub struct StubAST { pub nodes: Vec, @@ -84,20 +125,18 @@ impl StubAST { } pub fn push_comment(&mut self, comment: &str) -> Result<(), Error> { - //self.nodes.push(comment.to_string()); self.nodes.push(StubNodes::Comment { content: comment.to_string(), - meta: vec!(), + meta: IndexMap::new(), }); Ok(()) } pub fn push_vector(&mut self, timeset_id: usize, repeat: usize) -> Result<(), Error> { - //self.nodes.push(format!("Vector - Timeset ID: {}, Repeat: {}", timeset_id, repeat).to_string()); self.nodes.push(StubNodes::Vector { timeset_id: timeset_id, repeat: repeat, - meta: vec!(), + meta: IndexMap::new(), }); self.vector_count += 1; self.cycle_count += repeat; @@ -115,10 +154,6 @@ impl StubAST { pub fn len(&self) -> usize { self.nodes.len() } - - // pub fn get_node(&self, idx) { - - // } } #[derive(Debug)] @@ -183,93 +218,56 @@ impl Tester { pub fn set_timeset(&mut self, dut: &super::dut::Dut, model_id: usize, timeset_name: &str) -> Result<(), Error> { self.current_timeset_id = Some(dut._get_timeset(model_id, timeset_name)?.id); - self.issue_callbacks("set_timeset")?; Ok(()) } pub fn clear_timeset(&mut self) -> Result<(), Error> { self.current_timeset_id = Option::None; - self.issue_callbacks("clear_timeset")?; Ok(()) } - pub fn issue_callbacks(&mut self, func: &str) -> Result<(), Error> { - for g in self.target_generators.iter() { - match g { - Generators::DummyGeneratorWithInterceptors => { - let g_ = DummyGeneratorWithInterceptors{}; - if func == "cc" { - g_.cc(&mut self.ast)?; - } else if func == "cycle" { - g_.cycle(&mut self.ast)?; - }; - }, - _ => {} - } + pub fn issue_callback_at(&mut self, func: &str, idx: usize) -> Result<(), Error> { + let g = &self.target_generators[idx]; + match g { + Generators::DummyGeneratorWithInterceptors => { + let g_ = DummyGeneratorWithInterceptors{}; + if func == "cc" { + g_.cc(&mut self.ast)?; + } else if func == "cycle" { + g_.cycle(&mut self.ast)?; + }; + }, + _ => {} } Ok(()) } pub fn cc(&mut self, comment: &str) -> Result<(), Error> { self.ast.push_comment(comment)?; - self.issue_callbacks("cc")?; Ok(()) } pub fn cycle(&mut self, repeat: Option) -> Result<(), Error>{ - self.ast.push_vector(self._current_timeset_id()?, repeat.unwrap_or(1)); - self.issue_callbacks("cycle")?; + self.ast.push_vector(self._current_timeset_id()?, repeat.unwrap_or(1))?; Ok(()) } - pub fn generate(&mut self) -> Result { - let mut stat = GenerateStatus::new(); - let num_targets; - { - num_targets = self.target_generators.len(); - } - //for (i, g) in self.target_generators.iter().enumerate() { - for i in 0..num_targets { - let stat_ = self.generate_target_at(i)?; - stat.completed.extend(stat_.completed); - stat.external.extend(stat_.external); - // match g { - // Generators::DummyGenerator => { - // DummyGenerator{}.generate(&self.ast)?; - // stat.completed.push(Generators::DummyGenerator.to_string()) - // }, - // Generators::DummyGeneratorWithInterceptors => { - // DummyGeneratorWithInterceptors{}.generate(&self.ast)?; - // stat.completed.push(Generators::DummyGeneratorWithInterceptors.to_string()) - // } - // Generators::V93K_ST7 => { - // V93KGen{}.generate(&self.ast)?; - // stat.completed.push(Generators::V93K_ST7.to_string()) - // } - // Generators::External(gen) => { - // stat.external.push(gen.to_string()); - // } - // } - } - Ok(stat) - } - /// Generates the output for the target at index i. /// Allows the frontend to call generators in a loop. - pub fn generate_target_at(&mut self, idx: usize) -> Result { + pub fn generate_target_at(&mut self, idx: usize, dut: &Dut) -> Result { let mut stat = GenerateStatus::new(); let g = &self.target_generators[idx]; match g { Generators::DummyGenerator => { - DummyGenerator{}.generate(&self.ast)?; + DummyGenerator{}.generate(&self.ast, dut)?; stat.completed.push(Generators::DummyGenerator.to_string()) }, Generators::DummyGeneratorWithInterceptors => { - DummyGeneratorWithInterceptors{}.generate(&self.ast)?; + DummyGeneratorWithInterceptors{}.generate(&self.ast, dut)?; stat.completed.push(Generators::DummyGeneratorWithInterceptors.to_string()) } Generators::V93kSt7 => { - V93KGen{}.generate(&self.ast)?; + V93KGen{}.generate(&self.ast, dut)?; stat.completed.push(Generators::V93kSt7.to_string()) } Generators::External(gen) => { @@ -320,7 +318,6 @@ impl Tester { pub fn generators(&self) -> Vec { let mut gens = Generators::DummyGenerator.to_vector_strings(); - //let temp = self.external_generators.iter().map ( |n| n).collect::>(); gens.extend(self.external_generators.iter().map(|(n, _)| n.clone()).collect::>()); gens } @@ -340,24 +337,21 @@ impl GenerateStatus { } } -struct Generator { - name: String, - path: String, - -} - struct DummyGenerator {} impl DummyGenerator { /// A dummy generator which simply prints everything to the screen. - pub fn generate(&self, ast: &StubAST) -> Result<(), Error> { + pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { println!("Printing StubAST to console..."); for (i, n) in ast.nodes.iter().enumerate() { match n { - StubNodes::Comment {content, meta} => println!(" ::DummyGenerator Node {}: Comment - Content: {}", i, content), - StubNodes::Vector {timeset_id, repeat, meta} => println!(" ::DummyGenerator Node {}: Vector - Repeat: {}", i, repeat), - StubNodes::Node {meta} => println!(" ::DummyGenerator Node {}: Node", i), + StubNodes::Comment {content, ..} => println!(" ::DummyGenerator Node {}: Comment - Content: {}", i, content), + StubNodes::Vector {timeset_id, repeat, ..} => { + let t = &dut.timesets[*timeset_id]; + println!(" ::DummyGenerator Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); + }, + StubNodes::Node { .. } => println!(" ::DummyGenerator Node {}: Node", i), } } Ok(()) @@ -369,13 +363,16 @@ struct DummyGeneratorWithInterceptors {} impl DummyGeneratorWithInterceptors { /// A dummy generator which simply prints everything to the screen. - pub fn generate(&self, ast: &StubAST) -> Result<(), Error> { + pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { println!("Printing StubAST to console..."); for (i, n) in ast.nodes.iter().enumerate() { match n { - StubNodes::Comment {content, meta} => println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", i, content), - StubNodes::Vector {timeset_id, repeat, meta} => println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}", i, repeat), - StubNodes::Node {meta} => println!(" ::DummyGeneratorWithInterceptors Node {}: Node", i), + StubNodes::Comment {content, ..} => println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", i, content), + StubNodes::Vector {timeset_id, repeat, ..} => { + let t = &dut.timesets[*timeset_id]; + println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); + }, + StubNodes::Node { .. } => println!(" ::DummyGeneratorWithInterceptors Node {}: Node", i), } } Ok(()) @@ -384,7 +381,7 @@ impl DummyGeneratorWithInterceptors { pub fn cycle(&self, ast: &mut StubAST) -> Result<(), Error> { let n = ast.nodes.last_mut().unwrap(); match n { - StubNodes::Vector {timeset_id, repeat, meta} => { + StubNodes::Vector { .. } => { println!("Vector intercepted by DummyGeneratorWithInterceptors!"); Ok(()) }, @@ -399,7 +396,7 @@ impl DummyGeneratorWithInterceptors { StubNodes::Comment {content, meta} => { println!("Comment intercepted by DummyGeneratorWithInterceptors!"); n_ = StubNodes::Comment { - content: String::from(format!(" Comment intercepted by DummyGeneratorWithInterceptors! {}", content.clone())), + content: String::from(format!("Comment intercepted by DummyGeneratorWithInterceptors! {}", content.clone())), meta: meta.clone(), }; }, diff --git a/rust/origen/src/testers/mod.rs b/rust/origen/src/testers/mod.rs index d3f198d2..28905d76 100644 --- a/rust/origen/src/testers/mod.rs +++ b/rust/origen/src/testers/mod.rs @@ -1 +1,2 @@ -pub mod v93k; \ No newline at end of file +pub mod v93k; +pub mod simulator; \ No newline at end of file diff --git a/rust/origen/src/testers/simulator/mod.rs b/rust/origen/src/testers/simulator/mod.rs new file mode 100644 index 00000000..9e70d07f --- /dev/null +++ b/rust/origen/src/testers/simulator/mod.rs @@ -0,0 +1,32 @@ +use crate::core::tester::{StubAST}; +use crate::error::Error; +use crate::core::dut::{Dut}; + +pub struct Generator {} + +impl Generator { + /// The simulator's actions are done through the AST generation. There's actually nothing currently to do during generation. + pub fn generate(&self, _ast: &StubAST, _dut: &Dut) -> Result<(), Error> { + Ok(()) + } + + pub fn clear_timeset(&self, _dut: &Dut) -> Result<(), Error> { + println!(""); + Ok(()) + } + + pub fn set_timeset(&self, _timeset_id: usize, _dut: &Dut) -> Result<(), Error> { + println!(""); + Ok(()) + } + + pub fn cycle(&self, _ast: &mut StubAST, _dut: &Dut) -> Result<(), Error> { + println!(""); + Ok(()) + } + + pub fn cc(&self, _ast: &mut StubAST, _dut: &Dut) -> Result<(), Error> { + println!(""); + Ok(()) + } +} \ No newline at end of file diff --git a/rust/origen/src/testers/v93k/mod.rs b/rust/origen/src/testers/v93k/mod.rs index b0cdc22f..afd5a2bd 100644 --- a/rust/origen/src/testers/v93k/mod.rs +++ b/rust/origen/src/testers/v93k/mod.rs @@ -1,16 +1,20 @@ use crate::core::tester::{StubAST, StubNodes}; use crate::error::Error; +use crate::core::dut::{Dut}; pub struct Generator {} impl Generator { /// An extremely elementary generator for the 93. Won't actually generate anything of use yet. - pub fn generate(&self, ast: &StubAST) -> Result<(), Error> { - for (i, n) in ast.nodes.iter().enumerate() { + pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { + for (_i, n) in ast.nodes.iter().enumerate() { match n { - StubNodes::Comment {content, meta} => println!("# {}", content), - StubNodes::Vector {timeset_id, repeat, meta} => println!("R{} ;", repeat), - StubNodes::Node {meta} => return Err(Error::new(&format!("Pure meta nodes are not supported by the V93K yet!"))), + StubNodes::Comment {content, ..} => println!("# {}", content), + StubNodes::Vector {timeset_id, repeat, ..} => { + let t = &dut.timesets[*timeset_id]; + println!("R{} {} # ;", repeat, t.name); + }, + StubNodes::Node {..} => return Err(Error::new(&format!("Pure meta nodes are not supported by the V93K yet!"))), } } Ok(()) diff --git a/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs b/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs index ee4a410c..d4a3da9f 100644 --- a/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs +++ b/rust/pyapi/src/meta/py_like_apis/dict_like_api.rs @@ -56,8 +56,11 @@ pub trait DictLikeAPI { } fn values(&self) -> PyResult> { - let dut = DUT.lock().unwrap(); - let items = self.lookup_table(&dut); + let items; + { + let dut = DUT.lock().unwrap(); + items = self.lookup_table(&dut); + } let gil = Python::acquire_gil(); let py = gil.python(); @@ -69,8 +72,11 @@ pub trait DictLikeAPI { } fn items(&self) -> PyResult> { - let dut = DUT.lock().unwrap(); - let items = self.lookup_table(&dut); + let items; + { + let dut = DUT.lock().unwrap(); + items = self.lookup_table(&dut); + } let gil = Python::acquire_gil(); let py = gil.python(); @@ -85,33 +91,33 @@ pub trait DictLikeAPI { } fn get(&self, name: &str) -> PyResult { - let dut = DUT.lock().unwrap(); - let items = self.lookup_table(&dut); - let item = items.get(name); - let gil = Python::acquire_gil(); let py = gil.python(); - match item { - Some(_item) => Ok(self.new_pyitem(py, name, self.model_id())?), - None => Ok(py.None()) + { + let dut = DUT.lock().unwrap(); + let items = self.lookup_table(&dut); + if items.get(name).is_none() { + return Ok(py.None()); } + } + Ok(self.new_pyitem(py, name, self.model_id())?) } // Functions for PyMappingProtocol fn __getitem__(&self, name: &str) -> PyResult { - let dut = DUT.lock().unwrap(); - let items = self.lookup_table(&dut); - let item = items.get(name); - let gil = Python::acquire_gil(); let py = gil.python(); - match item { - Some(_item) => Ok(self.new_pyitem(py, name, self.model_id())?), - None => Err(pyo3::exceptions::KeyError::py_err(format!( - "No pin or pin alias found for {}", - name - ))), + { + let dut = DUT.lock().unwrap(); + let items = self.lookup_table(&dut); + if items.get(name).is_none() { + return Err(pyo3::exceptions::KeyError::py_err(format!( + "No item found for {}", + name + ))); + } } + Ok(self.new_pyitem(py, name, self.model_id())?) } fn __len__(&self) -> PyResult { @@ -121,8 +127,11 @@ pub trait DictLikeAPI { } fn __iter__(&self) -> PyResult { - let dut = DUT.lock().unwrap(); - let items = self.lookup_table(&dut); + let items; + { + let dut = DUT.lock().unwrap(); + items = self.lookup_table(&dut); + } Ok(DictLikeIter { keys: items.iter().map(|(s, _)| s.clone()).collect(), i: 0, diff --git a/rust/pyapi/src/meta/py_like_apis/list_like_api.rs b/rust/pyapi/src/meta/py_like_apis/list_like_api.rs index 5a88bc82..122a393d 100644 --- a/rust/pyapi/src/meta/py_like_apis/list_like_api.rs +++ b/rust/pyapi/src/meta/py_like_apis/list_like_api.rs @@ -17,8 +17,11 @@ pub trait ListLikeAPI { } fn ___getitem__(&self, idx: isize) -> PyResult { - let dut = DUT.lock().unwrap(); - let item_ids = self.item_ids(&dut); + let item_ids; + { + let dut = DUT.lock().unwrap(); + item_ids = self.item_ids(&dut); + } if idx >= (item_ids.len() as isize) { return Err(pyo3::exceptions::IndexError::py_err(format!( "Index {} is out range of container of size {}", idx, item_ids.len() @@ -41,9 +44,12 @@ pub trait ListLikeAPI { } fn ___getslice__(&self, slice: &PySlice) -> PyResult { - let dut = DUT.lock().unwrap(); - let item_ids = self.item_ids(&dut); - let indices = slice.indices((item_ids.len() as i32).into())?; + let indices; + { + let dut = DUT.lock().unwrap(); + let item_ids = self.item_ids(&dut); + indices = slice.indices((item_ids.len() as i32).into())?; + } let gil = Python::acquire_gil(); let py = gil.python(); diff --git a/rust/pyapi/src/tester.rs b/rust/pyapi/src/tester.rs index 7bc8440c..07d3bf09 100644 --- a/rust/pyapi/src/tester.rs +++ b/rust/pyapi/src/tester.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use origen::error::Error; use super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; use origen::core::tester::{StubNodes, Generators}; -//use pyo3::type_object::PyTypeObject; +use pyo3::types::IntoPyDict; #[pymodule] pub fn tester(_py: Python, m: &PyModule) -> PyResult<()> { @@ -20,7 +20,6 @@ pub fn tester(_py: Python, m: &PyModule) -> PyResult<()> { pub struct PyTester { python_generators: HashMap, metadata: Vec, - //py_ast: PyObject, } #[pymethods] @@ -31,41 +30,10 @@ impl PyTester { obj.init({ PyTester { python_generators: HashMap::new(), metadata: vec!(), - // py_ast: { - // let gil = Python::acquire_gil(); - // let py = gil.python(); - // Py::new(py, StubPyAST {}).unwrap().to_object(py) - // } } }); } - pub fn push_metadata(&mut self, item: &PyAny) -> usize { - let gil = Python::acquire_gil(); - let py = gil.python(); - - self.metadata.push(item.to_object(py)); - self.metadata.len() - 1 - } - - pub fn override_metadata_at(&mut self, idx: usize, item: &PyAny) -> PyResult<()> { - let gil = Python::acquire_gil(); - let py = gil.python(); - if self.metadata.len() > idx { - self.metadata[idx] = item.to_object(py); - Ok(()) - } else { - Err(PyErr::from(Error::new(&format!( - "Overriding metadata at {} exceeds the size of the current metadata vector!", - idx - )))) - } - } - - pub fn get_metadata(&self, idx: usize) -> PyResult<&PyObject> { - Ok(&self.metadata[idx]) - } - fn reset(slf: PyRef) -> PyResult { origen::tester().reset()?; @@ -107,24 +75,13 @@ impl PyTester { tester.clear_timeset()?; } return self.get_timeset(); - //return type_error!(format!("Test! (class '{}')", timeset.get_type().name())); } else if timeset.get_type().name().to_string() == "Timeset" { let gil = Python::acquire_gil(); let py = gil.python(); let obj = timeset.to_object(py); - //let m = obj.getattr(py, "__module__")?.extract::(py)?; - - //if m == "_origen.dut.timesets" { - model_id = obj.getattr(py, "__origen__model_id__")?.extract::(py)?; - timeset_name = obj.getattr(py, "name")?.extract::(py)?; - //} else { - // return type_error!(format!("Could not interpret 'timeset' argument as _origen.dut.timesets.Timeset object! It appears to be of type 'Timeset', but of module '{}'", m)); - //} + model_id = obj.getattr(py, "__origen__model_id__")?.extract::(py)?; + timeset_name = obj.getattr(py, "name")?.extract::(py)?; } else { - //let obj = timeset.to_object(); // timeset.get_type().name().to_string() == "_origen.dut.timesets.Timeset" - //if obj.call0() - //let t = pyo3::type_object::PyTypeObject::type_object(timeset); - //timeset.get_type().is_instance(); return type_error!(format!("Could not interpret 'timeset' argument as String or _origen.dut.timesets.Timeset object! (class '{}')", timeset.get_type().name())); } } @@ -135,10 +92,6 @@ impl PyTester { tester.set_timeset(&dut, model_id, ×et_name)?; } self.get_timeset() - // let model = dut.get_model(model_id).unwrap(); - // let gil = Python::acquire_gil(); - // let py = gil.python(); - // pytimeset!(py, model, model_id, ×et_name) } fn set_timeset(&self, timeset: &PyAny) -> PyResult { @@ -146,25 +99,116 @@ impl PyTester { } fn cc(slf: PyRef, comment: &str) -> PyResult { - let mut tester = origen::tester(); - tester.cc(&comment)?; + { + let mut tester = origen::tester(); + tester.cc(&comment)?; + } + slf.issue_callbacks("cc")?; let gil = Python::acquire_gil(); let py = gil.python(); Ok(slf.to_object(py)) } + fn issue_callbacks(&self, func: &str) -> PyResult<()> { + // Get the current targeted generators + let targets; + { + let tester = origen::tester(); + targets = tester.targets().clone(); + } + + // issue callbacks in the order which they were targeted + for (i, t) in targets.iter().enumerate() { + match t { + Generators::External(g) => { + // External generators which the backend can't generate itself. Need to generate them here. + match self.python_generators.get(&(g.clone())) { + Some(gen) => { + // The generator here is a PyObject - a handle on the class itself. + // Instantiate it and call its generate method with the AST. + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = gen.call0(py)?; + let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); + + // Note: We could just try the callback on the generator and ignore an attribute error, but this ignores any attribute errors that + // may occur inside of the callback itself. So, check first if the attribute exists, so we know we're calling it. + let has_attr = inst.getattr(py, func); + + // the above attr either didn't throw an error or threw an attribute error, then carry on. + // Otherwise, something unexpected occured. Throw that error. + match has_attr { + Err(e) => { + if !e.is_instance::(py) { + return Err(PyErr::from(e)); + } + }, + _ => { + inst.call_method1(py, func, args)?; + }, + } + }, + None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), + } + }, + _ => { + let mut tester = origen::tester(); + tester.issue_callback_at(func, i)?; + } + } + } + Ok(()) + } + /// Expecting more arguments/options to eventually be added here. #[args(kwargs="**")] fn cycle(slf: PyRef, kwargs: Option<&PyDict>) -> PyResult { - let mut tester = origen::tester(); - let mut repeat = None; - if let Some(_kwargs) = kwargs { - if let Some(_kwarg) = _kwargs.get_item("repeat") { - repeat = Some(_kwarg.extract::()?); + let targets; + { + let mut tester = origen::tester(); + let mut repeat = None; + if let Some(_kwargs) = kwargs { + if let Some(_kwarg) = _kwargs.get_item("repeat") { + repeat = Some(_kwarg.extract::()?); + } + } + tester.cycle(repeat)?; + targets = tester.targets().clone(); + } + + // issue callbacks + for (i, t) in targets.iter().enumerate() { + match t { + Generators::External(g) => { + // External generators which the backend can't generate itself. Need to generate them here. + match slf.python_generators.get(&(g.clone())) { + Some(gen) => { + // The generator here is a PyObject - a handle on the class itself. + // Instantiate it and call its generate method with the AST. + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = gen.call0(py)?; + let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); + let r = inst.call_method1(py, "cycle", args); + match r { + Err(p) => { + if !p.is_instance::(py) { + return Err(PyErr::from(p)); + } + }, + _ => {}, + } + }, + None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), + } + }, + _ => { + let mut tester = origen::tester(); + tester.issue_callback_at("cycle", i)?; + } } } - tester.cycle(repeat)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -181,19 +225,14 @@ impl PyTester { fn register_generator(&mut self, g: &PyAny) -> PyResult<()> { let mut tester = origen::tester(); - //let name = generator.get_type().name().to_string(); - //let klass = g.downcast_ref::()?; - //let name = klass.name().to_string(); //g.name().to_string(); let gil = Python::acquire_gil(); let py = gil.python(); let obj = g.to_object(py); let mut n = obj.getattr(py, "__module__")?.extract::(py)?; n.push_str(&format!(".{}", obj.getattr(py, "__qualname__")?.extract::(py)?)); - //let name = klass_name.extract::(py)?; tester.register_external_generator(&n)?; - //let t = g.downcast_ref::()?; self.python_generators.insert(n, obj); Ok(()) } @@ -207,12 +246,6 @@ impl PyTester { if let Ok(name) = g.extract::() { tester.target(&name)?; } else { - // let gil = Python::acquire_gil(); - // let py = gil.python(); - // let klass = g.to_object(py); - // let klass = g.downcast_ref::()?; - // let name = klass.name().to_string(); //g.name().to_string(); - // tester.target(&name)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -241,19 +274,12 @@ impl PyTester { Ok(slf.to_object(py)) } - // fn active_generator(&self) -> PyResult { - // // ... - // } - fn generate(&self) -> PyResult<()> { - //let stat; - let mut targets: Vec = vec!(); + let targets; { - let mut tester = origen::tester(); - //stat = tester.generate()?; + let tester = origen::tester(); targets = tester.targets().clone(); } - //for g in stat.external { for (i, t) in targets.iter().enumerate() { match t { Generators::External(g) => { @@ -264,9 +290,6 @@ impl PyTester { // Instantiate it and call its generate method with the AST. let gil = Python::acquire_gil(); let py = gil.python(); - //let klass = gen.to_object(py); - //let obj = gen.call_method0(py, "__new__", )?; - //gen.call_method0(py, "__init__")?; let inst = gen.call0(py)?; let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); inst.call_method1(py, "generate", args)?; @@ -276,7 +299,8 @@ impl PyTester { }, _ => { let mut tester = origen::tester(); - tester.generate_target_at(i)?; + let dut = origen::DUT.lock().unwrap(); + tester.generate_target_at(i, &dut)?; } } } @@ -291,7 +315,6 @@ impl PyTester { #[getter] fn ast(&self) -> PyResult { - //Ok(self.py_ast) let gil = Python::acquire_gil(); let py = gil.python(); Ok(Py::new(py, StubPyAST {})?.to_object(py)) @@ -299,16 +322,31 @@ impl PyTester { } impl PyTester { + pub fn push_metadata(&mut self, item: &PyAny) -> usize { + let gil = Python::acquire_gil(); + let py = gil.python(); - // /// Retrieves a built timeset object from the Origen backend. - // fn origen_timeset(&self) -> Timeset { - // // ... - // } + self.metadata.push(item.to_object(py)); + self.metadata.len() - 1 + } + + pub fn override_metadata_at(&mut self, idx: usize, item: &PyAny) -> PyResult<()> { + let gil = Python::acquire_gil(); + let py = gil.python(); + if self.metadata.len() > idx { + self.metadata[idx] = item.to_object(py); + Ok(()) + } else { + Err(PyErr::from(Error::new(&format!( + "Overriding metadata at {} exceeds the size of the current metadata vector!", + idx + )))) + } + } - // /// Retrieves a built timeset object from the PyAPI. - // fn pyorigen_timeset(&self) -> Timeset { - // // ... - // } + pub fn get_metadata(&self, idx: usize) -> PyResult<&PyObject> { + Ok(&self.metadata[idx]) + } } #[pyclass] @@ -354,15 +392,28 @@ impl ListLikeAPI for StubPyAST { let node = &ast.nodes[idx]; let dict = PyDict::new(py); match node { - StubNodes::Comment {content, meta} => { + StubNodes::Comment {content, ..} => { dict.set_item("type", "comment")?; dict.set_item("content", content)?; }, - StubNodes::Vector {timeset_id, repeat, meta} => { + StubNodes::Vector {timeset_id, repeat, ..} => { + let (model_id, name); + { + let dut = origen::dut(); + let tset = &dut.timesets[*timeset_id]; + model_id = tset.model_id; + name = tset.name.clone(); + } + let t = Py::new(py, Timeset { + name: name, + model_id: model_id + }).unwrap().to_object(py); + + dict.set_item("timeset", t)?; dict.set_item("type", "vector")?; dict.set_item("repeat", repeat)?; }, - StubNodes::Node {meta} => { + StubNodes::Node {..} => { dict.set_item("type", "node")?; }, } @@ -408,6 +459,104 @@ impl PyNode { } } +#[pymethods] +impl PyNode { + fn add_metadata(&self, id_str: &str, obj: &PyAny) -> PyResult<()> { + let mut tester = origen::tester(); + let ast = tester.get_mut_ast(); + let node = &mut ast.nodes[self.idx]; + + let gil = Python::acquire_gil(); + let py = gil.python(); + let locals = [("origen", py.import("origen")?)].into_py_dict(py); + let pytester = py + .eval("origen.tester", None, Some(&locals)) + .unwrap() + .downcast_mut::()?; + let idx = pytester.push_metadata(obj); + + node.add_metadata_id(id_str, idx)?; + Ok(()) + } + + fn get_metadata(&self, id_str: &str) -> PyResult { + let tester = origen::tester(); + let ast = tester.get_ast(); + let node = &ast.nodes[self.idx]; + + let gil = Python::acquire_gil(); + let py = gil.python(); + match node.get_metadata_id(id_str) { + Some(idx) => { + let locals = [("origen", py.import("origen")?)].into_py_dict(py); + let pytester = py + .eval("origen.tester", None, Some(&locals)) + .unwrap() + .downcast_mut::()?; + let obj = pytester.get_metadata(idx)?; + Ok(obj.to_object(py)) + } + None => Ok(py.None()), + } + } + + fn set_metadata(&self, id_str: &str, obj: &PyAny) -> PyResult { + let mut tester = origen::tester(); + let ast = tester.get_mut_ast(); + let node = &mut ast.nodes[self.idx]; + + let gil = Python::acquire_gil(); + let py = gil.python(); + let locals = [("origen", py.import("origen")?)].into_py_dict(py); + let pytester = py + .eval("origen.tester", None, Some(&locals)) + .unwrap() + .downcast_mut::()?; + match node.get_metadata_id(id_str) { + Some(idx) => { + pytester.override_metadata_at(idx, obj)?; + Ok(true) + } + None => { + let idx = pytester.push_metadata(obj); + node.add_metadata_id(id_str, idx)?; + Ok(false) + } + } + } + + // This is more of just a prototype at this point to ensure things like this will work. + fn set(&self, field: &str, value: &PyAny) -> PyResult<()> { + let mut tester = origen::tester(); + let ast = tester.get_mut_ast(); + let node = &mut ast.nodes[self.idx]; + let node_; + + match node { + StubNodes::Comment {content: _, meta} => { + match field { + "content" => { + node_ = StubNodes::Comment { + content: value.extract::()?, + meta: meta.clone(), + }; + }, + _ => return Err(PyErr::from(Error::new(&format!("Node type 'comment' does not have field '{}'", field)))) + } + }, + StubNodes::Vector {timeset_id: _, repeat: _, meta: _} => { + return Err(PyErr::from(Error::new(&format!("Node type 'vector' does not have field '{}'", field)))) + }, + StubNodes::Node {..} => { + return Err(PyErr::from(Error::new(&format!("Node type 'node' does not have field '{}'", field)))) + }, + } + drop(node); + ast.nodes[self.idx] = node_; + Ok(()) + } +} + impl PyNode { pub fn new(idx: usize, dict: PyObject) -> Self { Self { From f729f5dee0fd35550106bb0c7665af73c9d59827 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Wed, 12 Feb 2020 18:49:40 -0600 Subject: [PATCH 03/18] Some additional tests and reorg --- example/tests/shared/__init__.py | 2 +- example/tests/tester_test.py | 54 +++++++++++- rust/origen/src/core/tester.rs | 103 ++++++----------------- rust/origen/src/testers/mod.rs | 77 ++++++++++++++++- rust/origen/src/testers/simulator/mod.rs | 2 +- rust/pyapi/src/tester.rs | 16 ++-- 6 files changed, 165 insertions(+), 89 deletions(-) diff --git a/example/tests/shared/__init__.py b/example/tests/shared/__init__.py index 5a64f8b2..8f2753c9 100644 --- a/example/tests/shared/__init__.py +++ b/example/tests/shared/__init__.py @@ -19,5 +19,5 @@ def clean_tester(): origen.tester.reset() assert origen.tester.targets == [] assert len(origen.tester.ast) == 0 - assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7"] + assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7", "::Simulator"] assert origen.tester.timeset is None \ No newline at end of file diff --git a/example/tests/tester_test.py b/example/tests/tester_test.py index 0f2f5ce5..5c547acc 100644 --- a/example/tests/tester_test.py +++ b/example/tests/tester_test.py @@ -1,7 +1,7 @@ import pytest import origen, _origen # pylint: disable=import-error -from shared import clean_eagle, clean_tester -from shared.python_like_apis import Fixture_ListLikeAPI +from tests.shared import clean_eagle, clean_tester # pylint: disable=import-error +from tests.shared.python_like_apis import Fixture_ListLikeAPI # pylint: disable=import-error # Test generator used to test the frontend <-> backend generator hooks class PyTestGenerator: @@ -81,7 +81,7 @@ def test_init_state(clean_eagle, clean_tester): assert origen.tester assert origen.tester.targets == [] assert len(origen.tester.ast) == 0 - assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7"] + assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7", "::Simulator"] assert origen.tester.timeset is None def test_setting_a_timeset(clean_eagle, clean_tester): @@ -304,3 +304,51 @@ def test_meta_generator(self, capfd, clean_eagle, clean_tester): "" ]) assert err == "" + +def test_targeted_generator_ordering(capfd, clean_eagle, clean_tester): + origen.tester.register_generator(PyTestGeneratorWithInterceptor) + origen.tester.target(PyTestGeneratorWithInterceptor) + origen.tester.target("::DummyGeneratorWithInterceptors") + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Comment intercepted by DummyGeneratorWithInterceptors!", + "Vector intercepted by DummyGeneratorWithInterceptors!", + "Comment intercepted by DummyGeneratorWithInterceptors!", + "Printing StubPyAST to console...", + " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern Start!", + " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", + " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern End!", + "Printing StubAST to console...", + " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern Start!", + " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern End!", + "" + ]) + assert err == "" + +def test_targeted_generator_reverse_ordering(capfd, clean_eagle, clean_tester): + origen.tester.register_generator(PyTestGeneratorWithInterceptor) + origen.tester.target("::DummyGeneratorWithInterceptors") + origen.tester.target(PyTestGeneratorWithInterceptor) + origen.tester.set_timeset("simple") + run_pattern() + origen.tester.generate() + out, err = capfd.readouterr() + assert out == "\n".join([ + "Comment intercepted by DummyGeneratorWithInterceptors!", + "Vector intercepted by DummyGeneratorWithInterceptors!", + "Comment intercepted by DummyGeneratorWithInterceptors!", + "Printing StubAST to console...", + " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern Start!", + " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern End!", + "Printing StubPyAST to console...", + " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern Start!", + " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", + " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern End!", + "" + ]) + assert err == "" diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs index 20f2f222..43fa5131 100644 --- a/rust/origen/src/core/tester.rs +++ b/rust/origen/src/core/tester.rs @@ -2,6 +2,8 @@ use crate::error::Error; use super::model::timesets::timeset::{Timeset}; use indexmap::IndexMap; use crate::testers::v93k::Generator as V93KGen; +use crate::testers::simulator::Generator as Simulator; +use crate::testers::{DummyGeneratorWithInterceptors, DummyGenerator}; use crate::core::dut::{Dut}; #[derive(Debug, PartialEq, Eq, Clone)] @@ -9,6 +11,7 @@ pub enum Generators { DummyGenerator, DummyGeneratorWithInterceptors, V93kSt7, + Simulator, //J750, //Uflex, External(String), @@ -20,6 +23,7 @@ impl Generators { "::DummyGenerator" => Some(Self::DummyGenerator), "::DummyGeneratorWithInterceptors" => Some(Self::DummyGeneratorWithInterceptors), "::V93K::ST7" => Some(Self::V93kSt7), + "::Simulator" => Some(Self::Simulator), _ => None } } @@ -29,12 +33,13 @@ impl Generators { Self::DummyGenerator => "::DummyGenerator".to_string(), Self::DummyGeneratorWithInterceptors => "::DummyGeneratorWithInterceptors".to_string(), Self::V93kSt7 => "::V93K::ST7".to_string(), + Self::Simulator => "::Simulator".to_string(), Self::External(g) => g.clone(), } } pub fn to_vector_strings(&self) -> Vec { - vec!("::DummyGenerator".to_string(), "::DummyGeneratorWithInterceptors".to_string(), "::V93K::ST7".to_string()) + vec!("::DummyGenerator".to_string(), "::DummyGeneratorWithInterceptors".to_string(), "::V93K::ST7".to_string(), "::Simulator".to_string()) } } @@ -200,7 +205,7 @@ impl Tester { Ok(()) } - pub fn get_timeset(&self, dut: &super::dut::Dut) -> Option { + pub fn get_timeset(&self, dut: &Dut) -> Option { if let Some(t_id) = self.current_timeset_id { Some(dut.timesets[t_id].clone()) } else { @@ -208,7 +213,7 @@ impl Tester { } } - pub fn _get_timeset(&self, dut: &super::dut::Dut) -> Result { + pub fn _get_timeset(&self, dut: &Dut) -> Result { if let Some(t_id) = self.current_timeset_id { Ok(dut.timesets[t_id].clone()) } else { @@ -216,7 +221,7 @@ impl Tester { } } - pub fn set_timeset(&mut self, dut: &super::dut::Dut, model_id: usize, timeset_name: &str) -> Result<(), Error> { + pub fn set_timeset(&mut self, dut: &Dut, model_id: usize, timeset_name: &str) -> Result<(), Error> { self.current_timeset_id = Some(dut._get_timeset(model_id, timeset_name)?.id); Ok(()) } @@ -226,7 +231,7 @@ impl Tester { Ok(()) } - pub fn issue_callback_at(&mut self, func: &str, idx: usize) -> Result<(), Error> { + pub fn issue_callback_at(&mut self, func: &str, idx: usize, dut: &Dut) -> Result<(), Error> { let g = &self.target_generators[idx]; match g { Generators::DummyGeneratorWithInterceptors => { @@ -237,6 +242,16 @@ impl Tester { g_.cycle(&mut self.ast)?; }; }, + Generators::Simulator => { + let g_ = Simulator{}; + match func { + "cc" => g_.cc(&mut self.ast, dut)?, + "cycle" => g_.cycle(&mut self.ast, dut)?, + "set_timeset" => g_.set_timeset(self.current_timeset_id, dut)?, + "clear_timeset" => g_.clear_timeset(dut)?, + _ => {}, // Simulator does not have callbacks for other functions + } + } _ => {} } Ok(()) @@ -270,6 +285,10 @@ impl Tester { V93KGen{}.generate(&self.ast, dut)?; stat.completed.push(Generators::V93kSt7.to_string()) } + Generators::Simulator => { + Simulator{}.generate(&self.ast, dut)?; + stat.completed.push(Generators::Simulator.to_string()) + } Generators::External(gen) => { stat.external.push(gen.to_string()); } @@ -335,76 +354,4 @@ impl GenerateStatus { external: vec!(), } } -} - -struct DummyGenerator {} - -impl DummyGenerator { - - /// A dummy generator which simply prints everything to the screen. - pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { - println!("Printing StubAST to console..."); - for (i, n) in ast.nodes.iter().enumerate() { - match n { - StubNodes::Comment {content, ..} => println!(" ::DummyGenerator Node {}: Comment - Content: {}", i, content), - StubNodes::Vector {timeset_id, repeat, ..} => { - let t = &dut.timesets[*timeset_id]; - println!(" ::DummyGenerator Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); - }, - StubNodes::Node { .. } => println!(" ::DummyGenerator Node {}: Node", i), - } - } - Ok(()) - } -} - -struct DummyGeneratorWithInterceptors {} - -impl DummyGeneratorWithInterceptors { - - /// A dummy generator which simply prints everything to the screen. - pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { - println!("Printing StubAST to console..."); - for (i, n) in ast.nodes.iter().enumerate() { - match n { - StubNodes::Comment {content, ..} => println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", i, content), - StubNodes::Vector {timeset_id, repeat, ..} => { - let t = &dut.timesets[*timeset_id]; - println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); - }, - StubNodes::Node { .. } => println!(" ::DummyGeneratorWithInterceptors Node {}: Node", i), - } - } - Ok(()) - } - - pub fn cycle(&self, ast: &mut StubAST) -> Result<(), Error> { - let n = ast.nodes.last_mut().unwrap(); - match n { - StubNodes::Vector { .. } => { - println!("Vector intercepted by DummyGeneratorWithInterceptors!"); - Ok(()) - }, - _ => Err(Error::new(&format!("Error Intercepting Vector! Expected vector node!"))) - } - } - - pub fn cc(&self, ast: &mut StubAST) -> Result<(), Error> { - let n = ast.nodes.last().unwrap(); - let n_; - match n { - StubNodes::Comment {content, meta} => { - println!("Comment intercepted by DummyGeneratorWithInterceptors!"); - n_ = StubNodes::Comment { - content: String::from(format!("Comment intercepted by DummyGeneratorWithInterceptors! {}", content.clone())), - meta: meta.clone(), - }; - }, - _ => return Err(Error::new(&format!("Error Intercepting Comment! Expected comment node!"))) - } - drop(n); - let i = ast.len() - 1; - ast.nodes[i] = n_; - Ok(()) - } -} +} \ No newline at end of file diff --git a/rust/origen/src/testers/mod.rs b/rust/origen/src/testers/mod.rs index 28905d76..9578f04e 100644 --- a/rust/origen/src/testers/mod.rs +++ b/rust/origen/src/testers/mod.rs @@ -1,2 +1,77 @@ pub mod v93k; -pub mod simulator; \ No newline at end of file +pub mod simulator; +use crate::core::tester::{StubAST, StubNodes}; +use crate::error::Error; +use crate::core::dut::{Dut}; + +pub struct DummyGenerator {} + +impl DummyGenerator { + + /// A dummy generator which simply prints everything to the screen. + pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { + println!("Printing StubAST to console..."); + for (i, n) in ast.nodes.iter().enumerate() { + match n { + StubNodes::Comment {content, ..} => println!(" ::DummyGenerator Node {}: Comment - Content: {}", i, content), + StubNodes::Vector {timeset_id, repeat, ..} => { + let t = &dut.timesets[*timeset_id]; + println!(" ::DummyGenerator Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); + }, + StubNodes::Node { .. } => println!(" ::DummyGenerator Node {}: Node", i), + } + } + Ok(()) + } +} + +pub struct DummyGeneratorWithInterceptors {} + +impl DummyGeneratorWithInterceptors { + + /// A dummy generator which simply prints everything to the screen. + pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { + println!("Printing StubAST to console..."); + for (i, n) in ast.nodes.iter().enumerate() { + match n { + StubNodes::Comment {content, ..} => println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", i, content), + StubNodes::Vector {timeset_id, repeat, ..} => { + let t = &dut.timesets[*timeset_id]; + println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); + }, + StubNodes::Node { .. } => println!(" ::DummyGeneratorWithInterceptors Node {}: Node", i), + } + } + Ok(()) + } + + pub fn cycle(&self, ast: &mut StubAST) -> Result<(), Error> { + let n = ast.nodes.last_mut().unwrap(); + match n { + StubNodes::Vector { .. } => { + println!("Vector intercepted by DummyGeneratorWithInterceptors!"); + Ok(()) + }, + _ => Err(Error::new(&format!("Error Intercepting Vector! Expected vector node!"))) + } + } + + pub fn cc(&self, ast: &mut StubAST) -> Result<(), Error> { + let n = ast.nodes.last().unwrap(); + let n_; + match n { + StubNodes::Comment {content, meta} => { + println!("Comment intercepted by DummyGeneratorWithInterceptors!"); + n_ = StubNodes::Comment { + content: String::from(format!("Comment intercepted by DummyGeneratorWithInterceptors! {}", content.clone())), + meta: meta.clone(), + }; + }, + _ => return Err(Error::new(&format!("Error Intercepting Comment! Expected comment node!"))) + } + drop(n); + let i = ast.len() - 1; + ast.nodes[i] = n_; + Ok(()) + } +} diff --git a/rust/origen/src/testers/simulator/mod.rs b/rust/origen/src/testers/simulator/mod.rs index 9e70d07f..4bdab353 100644 --- a/rust/origen/src/testers/simulator/mod.rs +++ b/rust/origen/src/testers/simulator/mod.rs @@ -15,7 +15,7 @@ impl Generator { Ok(()) } - pub fn set_timeset(&self, _timeset_id: usize, _dut: &Dut) -> Result<(), Error> { + pub fn set_timeset(&self, _timeset_id: Option, _dut: &Dut) -> Result<(), Error> { println!(""); Ok(()) } diff --git a/rust/pyapi/src/tester.rs b/rust/pyapi/src/tester.rs index 07d3bf09..ff4710f0 100644 --- a/rust/pyapi/src/tester.rs +++ b/rust/pyapi/src/tester.rs @@ -74,6 +74,7 @@ impl PyTester { let mut tester = origen::TESTER.lock().unwrap(); tester.clear_timeset()?; } + self.issue_callbacks("clear_timeset")?; return self.get_timeset(); } else if timeset.get_type().name().to_string() == "Timeset" { let gil = Python::acquire_gil(); @@ -87,9 +88,12 @@ impl PyTester { } { - let mut tester = origen::TESTER.lock().unwrap(); - let dut = origen::DUT.lock().unwrap(); - tester.set_timeset(&dut, model_id, ×et_name)?; + { + let mut tester = origen::TESTER.lock().unwrap(); + let dut = origen::DUT.lock().unwrap(); + tester.set_timeset(&dut, model_id, ×et_name)?; + } + self.issue_callbacks("set_timeset")?; } self.get_timeset() } @@ -154,7 +158,8 @@ impl PyTester { }, _ => { let mut tester = origen::tester(); - tester.issue_callback_at(func, i)?; + let dut = origen::dut(); + tester.issue_callback_at(func, i, &dut)?; } } } @@ -205,7 +210,8 @@ impl PyTester { }, _ => { let mut tester = origen::tester(); - tester.issue_callback_at("cycle", i)?; + let dut = origen::dut(); + tester.issue_callback_at("cycle", i, &dut)?; } } } From dd24dde678729395dbe9bf3f45441786d030494c Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Mon, 17 Feb 2020 06:58:20 -0600 Subject: [PATCH 04/18] Reorganize pins under the DUT, as timesets, register, etc. are --- example/tests/pin_api_test.py | 9 +- rust/origen/src/core/dut.rs | 21 +- rust/origen/src/core/model/mod.rs | 13 +- rust/origen/src/core/model/pins.rs | 656 +++++++++--------- rust/origen/src/core/model/pins/pin.rs | 16 +- .../src/core/model/pins/pin_collection.rs | 30 +- rust/origen/src/core/model/pins/pin_group.rs | 98 +-- rust/pyapi/src/pins.rs | 71 +- rust/pyapi/src/pins/physical_pin_container.rs | 157 +---- rust/pyapi/src/pins/pin.rs | 91 +-- rust/pyapi/src/pins/pin_collection.rs | 65 +- rust/pyapi/src/pins/pin_container.rs | 156 +---- rust/pyapi/src/pins/pin_group.rs | 185 ++--- 13 files changed, 649 insertions(+), 919 deletions(-) diff --git a/example/tests/pin_api_test.py b/example/tests/pin_api_test.py index 29e24b2d..949fa247 100644 --- a/example/tests/pin_api_test.py +++ b/example/tests/pin_api_test.py @@ -57,7 +57,6 @@ def test_pin_group_default_state(): p1 = origen.dut.pin("p1") assert p1.name == "p1" assert len(p1) == 1 - assert p1._path == "" assert p1.data == 0 assert p1.pin_actions == "Z" assert p1.pin_names == ["p1"] @@ -94,11 +93,10 @@ def test_grouping_pins(): grp = origen.dut.group_pins("grp", "p1", "p2", "p3") is_pin_group(grp) assert grp.name == "grp" - assert grp._path == "" + assert grp.pin_names == ["p1", "p2", "p3"] assert len(origen.dut.pins) == 4 assert len(grp) == 3 assert len(origen.dut.physical_pins) == 3 - assert grp.pin_names == ["p1", "p2", "p3"] assert grp.data == 0 assert grp.pin_actions == "ZZZ" @@ -214,23 +212,20 @@ def test_pins_in_subblocks(): assert len(origen.dut.pins) == 4 assert len(origen.dut.sub_blocks["core1"].pins) == 0 - # Add a pin at the subblock. Check it was added and has the correct path. + # Add a pin at the subblock. assert origen.dut.sub_blocks["core1"].add_pin("p1") assert len(origen.dut.sub_blocks["core1"].pins) == 1 p = origen.dut.sub_blocks["core1"].pin("p1") is_pin_group(p) - #assert p._path == "core1" # Add another pin assert origen.dut.sub_blocks["core1"].add_pin("_p1") assert len(origen.dut.sub_blocks["core1"].pins) == 2 _p = origen.dut.sub_blocks["core1"].pin("_p1") is_pin_group(_p) - #assert _p._path == "core1" # Verify the pins at origen.dut are unchanged. assert len(origen.dut.pins) == 4 - assert origen.dut.pin("p1")._path == "" assert origen.dut.pin("_p1") is None def test_adding_aliases(): diff --git a/rust/origen/src/core/dut.rs b/rust/origen/src/core/dut.rs index 631102b2..fec01898 100644 --- a/rust/origen/src/core/dut.rs +++ b/rust/origen/src/core/dut.rs @@ -2,6 +2,8 @@ use crate::core::model::registers::{ AccessType, AddressBlock, Bit, MemoryMap, Register, RegisterFile, }; use crate::core::model::timesets::timeset::{Event, Timeset, Wave, WaveGroup, Wavetable}; +use crate::core::model::pins::pin::{Pin}; +use crate::core::model::pins::pin_group::{PinGroup}; use crate::core::model::Model; use crate::error::Error; use crate::meta::IdGetters; @@ -50,9 +52,21 @@ use std::sync::RwLock; return_type = "Event", field_container_name = "wave_events" )] +#[id_getters_by_mapping( + field = "pin", + parent_field = "models", + return_type = "Pin", + field_container_name = "pins" +)] +#[id_getters_by_mapping( + field = "pin_group", + parent_field = "models", + return_type = "PinGroup", + field_container_name = "pin_groups" +)] pub struct Dut { pub name: String, - models: Vec, + pub models: Vec, memory_maps: Vec, address_blocks: Vec, register_files: Vec, @@ -63,6 +77,8 @@ pub struct Dut { pub wave_groups: Vec, pub waves: Vec, pub wave_events: Vec, + pub pins: Vec, + pub pin_groups: Vec, pub id_mappings: Vec>, } @@ -84,7 +100,8 @@ impl Dut { wave_groups: Vec::::new(), waves: Vec::::new(), wave_events: Vec::::new(), - + pins: Vec::::new(), + pin_groups: Vec::::new(), id_mappings: Vec::>::new(), } } diff --git a/rust/origen/src/core/model/mod.rs b/rust/origen/src/core/model/mod.rs index 7317de9d..300fff58 100644 --- a/rust/origen/src/core/model/mod.rs +++ b/rust/origen/src/core/model/mod.rs @@ -5,11 +5,7 @@ use crate::error::Error; use crate::Dut; use crate::Result; use std::sync::MutexGuard; - use indexmap::map::IndexMap; -use pins::pin::Pin; -use pins::pin_group::PinGroup; -use std::collections::HashMap; #[derive(Debug)] pub struct Model { @@ -22,8 +18,9 @@ pub struct Model { /// All registers owned by this model are arranged within memory maps pub memory_maps: IndexMap, // Pins - pub physical_pins: HashMap, - pub pins: HashMap, + pub pins: IndexMap, + pub pin_groups: IndexMap, + pub timesets: IndexMap, // TODO: Levels // TODO: Specs @@ -53,8 +50,8 @@ impl Model { parent_id: parent_id, sub_blocks: IndexMap::new(), memory_maps: IndexMap::new(), - physical_pins: HashMap::new(), - pins: HashMap::new(), + pins: IndexMap::new(), + pin_groups: IndexMap::new(), timesets: IndexMap::new(), address_unit_bits: 8, base_address: match base_address { diff --git a/rust/origen/src/core/model/pins.rs b/rust/origen/src/core/model/pins.rs index 106fb9f1..0f338961 100644 --- a/rust/origen/src/core/model/pins.rs +++ b/rust/origen/src/core/model/pins.rs @@ -3,6 +3,7 @@ pub mod pin_collection; pub mod pin_group; use crate::error::Error; use std::convert::TryFrom; +use super::super::dut::Dut; extern crate regex; use regex::Regex; @@ -19,33 +20,102 @@ pub enum Endianness { } impl Model { - //** Functions for adding, aliasing, grouping, and collecting pins ** + pub fn register_pin( + &mut self, + pin_group_id: usize, + physical_pin_id: usize, + name: &str, + reset_data: Option, + reset_action: Option, + endianness: Option, + ) -> Result<(PinGroup, Pin), Error> { + let pin_group = PinGroup::new(self.id, pin_group_id, name.to_string(), vec!(name.to_string()), endianness); + let mut physical_pin = Pin::new(self.id, physical_pin_id, name.to_string(), reset_data, reset_action); + physical_pin.groups.insert(name.to_string(), 0); + self.pin_groups.insert(name.to_string(), pin_group_id); + self.pins.insert(name.to_string(), physical_pin_id); + Ok((pin_group, physical_pin)) + } + + pub fn register_pin_group( + &mut self, + pin_group_id: usize, + name: &str, + pins: Vec, + endianness: Option + ) -> Result { + let pin_group = PinGroup::new(self.id, pin_group_id, name.to_string(), pins, endianness); + self.pin_groups.insert(name.to_string(), pin_group_id); + Ok(pin_group) + } + pub fn get_pin_id(&self, name: &str) -> Option { + match self.pins.get(name) { + Some(t) => Some(*t), + None => None, + } + } + + pub fn get_pin_group_id(&self, name: &str) -> Option { + match self.pin_groups.get(name) { + Some(t) => Some(*t), + None => None, + } + } +} + +impl Dut { pub fn add_pin( &mut self, + model_id: usize, name: &str, - path: &str, width: Option, offset: Option, reset_data: Option, reset_action: Option, endianness: Option, - ) -> Result<&mut PinGroup, Error> { - if self.get_pin_group(name).is_some() { - return Err(Error::new(&format!( - "Can not add pin {} because it conflicts with a current pin or alias name!", - name - ))); - } + ) -> Result<&PinGroup, Error> { + // Check some of the parameters before we go much further. We can error out quickly if something is awry. + // Check the width and offset if !width.is_some() && offset.is_some() { return Err(Error::new(&format!( "Can not add pin {} with a given offset but no width option!", name ))); + } else if self.get_pin_group(model_id, name).is_some() { + return Err(Error::new(&format!( + "Pin '{}' already exists on model '{}'!", + name, + self.models[model_id].name + ))); } - let mut names: Vec = vec![]; - let (mut rdata, mut raction) = (Option::None, Option::None); + let mut rdata = None; + let mut raction: Option> = None; + + // Check that the given reset data fits within the width of the pins to add. + if let Some(r) = reset_data { + self.verify_data_fits(width.unwrap_or(1), r)?; + rdata = Some(r); + } + + // // Check that the given reset pin actions fit within the width of the pins to add and that they + // // are valid pin action characters. + if let Some(r) = reset_action { + let mut temp = r.into_bytes(); + temp.reverse(); + raction = Some(temp.clone()); + } + if raction.is_some() { + self.verify_action_string_fits(width.unwrap_or(1), &raction.clone().unwrap())?; + } + //raction2 = raction.clone(); + + // Resolve the names first - if there's a problem with one of the names, an error will generated here but passed up + // to the frontend, which should end the progrma. Howvever, the user could catch the exception, which would leave the + // backend here in half-complete state. + // Just to be safe, resolve and check the names first before adding anything. + let mut names: Vec = vec!(); if let Some(w) = width { if w < 1 { return Err(Error::new(&format!( @@ -53,121 +123,117 @@ impl Model { w ))); } - - let (mut rdata_, mut raction_, mut raction_i, mut offset_) = - (reset_data.unwrap_or(0), "".as_bytes(), w, 0); - self.verify_data_fits(w, rdata_)?; - let mut temp = String::from(""); - if let Some(o) = offset { - offset_ = o; - } - if let Some(r) = reset_action { - temp = r; - raction_ = temp.as_bytes(); - self.verify_action_string_fits(w, raction_)?; - } - for i in offset_..(offset_ + w) { - if reset_data.is_some() { - // Set the individual pin data one by one, shifting the available data by one each time. - rdata = Some(rdata_ & 0x1); - rdata_ >>= 1; - } - if temp != "" { - // Same with the pin actions, except we'll be 'shifting' by a character each time. - // Note: we're assuming an action string inline with how we'd read data, so we're actually reading and shifting out of the - // end of the string. - // Note: a single character can be given to apply all the same action to all pins in the width. - if raction_.len() > 1 { - raction = Some(PinActions::try_from(raction_[(raction_i - 1) as usize])?); - raction_i -= 1; - } else { - raction = Some(PinActions::try_from(raction_[0])?); - } - } - let _name = format!("{}{}", name, i); - let p = Pin::new(String::from(&_name), String::from(path), rdata, raction); - names.push(String::from(&p.name)); - self.pins.insert( - String::from(&_name), - PinGroup::new( - String::from(&_name), - String::from(path), - vec![String::from(&_name)], - endianness, - ), - ); - self.physical_pins.insert(String::from(&_name), p); - } - } else { - if let Some(d) = reset_data { - // Single bit, so data can't be > 2. - if d > 2 { + let o = offset.unwrap_or(0); + for i in o..(o + w) { + let n = format!("{}{}", name, i).to_string(); + if self.get_pin_group(model_id, name).is_some() { return Err(Error::new(&format!( - "Reset data of {} overflows available width (1)!", - d + "Can not add pin {}, derived by adding pin {} of width {} with offset {}, because it conflicts with a current pin or alias name!", + n, + name, + w, + o, ))); } - rdata = Option::Some(d); + names.push(n); } - if let Some(a) = reset_action { - raction = Some(PinActions::from_str(&a)?); + } else { + names.push(name.to_string()); + } + + // Checks passed, so add the pins. + let (mut pin_group_id, mut physical_pin_id); + { + pin_group_id = self.pin_groups.len(); + physical_pin_id = self.pins.len(); + } + { + let model = &mut self.models[model_id]; + let (mut rd, mut ra) = (None, None); + for (i, n) in names.iter().enumerate() { + if let Some(r) = rdata { + rd = Some(r & 0x1); + rdata = Some(r >> 1); + } + if raction.is_some() { + ra = Some(PinActions::try_from(raction.clone().unwrap()[i])?); + } + let (pin_group, mut physical_pin) = model.register_pin( + pin_group_id, + physical_pin_id, + &n, + rd, + ra, + endianness, + )?; + physical_pin.groups.insert(name.to_string(), i); + self.pin_groups.push(pin_group); + self.pins.push(physical_pin); + pin_group_id += 1; + physical_pin_id += 1; } - let p = Pin::new(String::from(name), String::from(path), rdata, raction); - names.push(String::from(&p.name)); - self.physical_pins.insert(String::from(name), p); } - let grp = PinGroup::new(String::from(name), String::from(path), names, endianness); - self.pins.insert(String::from(name), grp); - Ok(self.pins.get_mut(name).unwrap()) + if offset.is_some() || width.is_some() { + // Add the group containing all the pins we just added, with index/offset. + // But, the actual requested group hasn't been added yet. + // If the offset and width are None, then group has the provided name. + self.group_pins_by_name(model_id, name, names, endianness)?; + Ok(&self.pin_groups[pin_group_id]) + } else { + Ok(&self.pin_groups[pin_group_id-1]) + } } - pub fn add_pin_alias(&mut self, name: &str, alias: &str) -> Result<(), Error> { + pub fn add_pin_alias(&mut self, model_id: usize, name: &str, alias: &str) -> Result<(), Error> { // First, check that the pin exists. - if self.pins.contains_key(alias) { + if self.models[model_id].pin_groups.contains_key(alias) { return Err(Error::new(&format!( - "Could not alias pin {} to {}, as {} already exists!", + "Could not alias '{}' to '{}', as '{}' already exists!", name, alias, alias ))); } - let grp; - let names; - if let Some(p) = self.get_pin_group(name) { + let (grp, names, id); + if let Some(idx) = self.models[model_id].pin_groups.get(name) { + id = self.pin_groups.len(); + let p = &self.pin_groups[*idx]; grp = PinGroup::new( + model_id, + id, String::from(alias), - String::from(&p.path), p.pin_names.clone(), Option::Some(p.endianness), ); names = p.pin_names.clone(); } else { return Err(Error::new(&format!( - "Could not alias pin {} to {}, as pin {} doesn't exists!", + "Could not alias '{}' to '{}', as '{}' doesn't exists!", name, alias, name ))); } - for p in names.iter() { - let pin = self._pin(&p).unwrap(); - pin.aliases.push(String::from(alias)); + for n in names.iter() { + let pin = self._get_mut_pin(model_id, n)?; + pin.aliases.push(String::from(alias)); } - self.pins.insert(String::from(alias), grp); + self.models[model_id].pin_groups.insert(alias.to_string(), id); + self.pin_groups.push(grp); Ok(()) } - pub fn group_pins( + pub fn group_pins_by_name( &mut self, + model_id: usize, name: &str, - path: &str, pins: Vec, - endianness: Option, - ) -> Result<&mut PinGroup, Error> { - if self.get_pin_group(name).is_some() { - return Err(Error::new(&format!("Can not add pin group {} because it conflicts with a current pin group or alias name!", name))); + endianness: Option + ) -> Result<&PinGroup, Error> { + let id; + { + id = self.pin_groups.len(); } - - let mut physical_names: Vec = vec![]; + let mut physical_names: Vec = vec!(); for (i, pin_name) in pins.iter().enumerate() { - if let Some(p) = self.get_mut_physical_pin(pin_name) { + if let Some(p) = self.resolve_to_mut_physical_pin(model_id, pin_name) { if physical_names.contains(&p.name) { return Err(Error::new(&format!("Can not group pins under {} because pin (or an alias of) {} has already been added to the group!", name, p.name))); } else { @@ -179,114 +245,139 @@ impl Model { name, pin_name ))); } - if let Some(p) = self.get_pin_group(pin_name) { + if let Some(p) = self.get_pin_group(model_id, pin_name) { physical_names.extend_from_slice(&p.pin_names); } } - let grp = PinGroup::new( - String::from(name), - String::from(path), - physical_names, - endianness, - ); - self.pins.insert(String::from(name), grp); - Ok(self.pins.get_mut(name).unwrap()) - } - // ** Functions for retrieving pins from names and aliases ** + let model = &mut self.models[model_id]; + self.pin_groups.push(model.register_pin_group(id, name, physical_names, endianness)?); + Ok(&self.pin_groups[id]) + } - /// Gets an immutable reference to an existing PinGroup, or Option::None, if not found.. - pub fn get_pin_group(&self, name: &str) -> Option<&PinGroup> { - if let Some(pin) = self.pins.get(name) { - Option::Some(pin) - } else { - Option::None + pub fn get_pin_data(&self, model_id: usize, names: &Vec) -> Result { + let mut data = 0; + for n in names.iter().rev() { + let p = self._get_pin(model_id, n)?; + data = (data << 1) + p.data; } + Ok(data as u32) } - /// Gets a mutable reference to an existing pin group, or Option::None, if not found. - pub fn get_mut_pin_group(&mut self, name: &str) -> Option<&mut PinGroup> { - if let Some(pin) = self.pins.get_mut(name) { - Option::Some(pin) - } else { - Option::None + pub fn get_pin_reset_data(&self, model_id: usize, names: &Vec) -> Result { + let mut rdata = 0; + for n in names.iter().rev() { + let p = self._get_pin(model_id, n)?; + rdata = (rdata << 1) + p.reset_data.unwrap_or(0); } + Ok(rdata as u32) } - /// Gets an immutable reference to an existing PinGroup, or an Error is the pin group isn't found. - pub fn _get_pin_group(&self, name: &str) -> Result<&PinGroup, Error> { - match self.get_pin_group(name) { - Some(grp) => Ok(grp), - None => Err(Error::new(&format!( - "No pin group '{}' has been added!", - name - ))), + pub fn reset_pin_names(&mut self, model_id: usize, names: &Vec) -> Result<(), Error> { + for n in names.iter() { + let p = self._get_mut_pin(model_id, n)?; + p.reset(); } + Ok(()) } - /// Gets a mutable reference to an existing PinGroup, or an Error is the pin group isn't found. - pub fn _get_mut_pin_group(&mut self, name: &str) -> Result<&mut PinGroup, Error> { - match self.get_mut_pin_group(name) { - Some(grp) => Ok(grp), - None => Err(Error::new(&format!( - "No pin group '{}' has been added!", - name - ))), + pub fn set_pin_data( + &mut self, + model_id: usize, + names: &Vec, + data: u32, + mask: Option, + ) -> Result<(), Error> { + self.data_fits_in_pins(names, data)?; + + let mut d = data; + let mut m = (mask.unwrap_or(!(0 as usize))) as u32; + for n in names.iter() { + let p = self._get_mut_pin(model_id, n)?; + p.set_data(((d & 0x1) & (m & 0x1)) as u8)?; + d = d >> 1; + m = m >> 1; } + Ok(()) } - pub fn _pin(&mut self, name: &str) -> Result<&mut Pin, Error> { - match self.physical_pins.get_mut(name) { - Some(p) => Ok(p), - None => Err(Error::new(&format!("Cannot find phyiscal pin {}! This signals either a bug in Origen or the backend model has been changed unexpectedly and this reference is stale.", name))), + pub fn get_pin_actions(&self, model_id: usize, names: &Vec) -> Result { + let mut s = String::from(""); + for n in names.iter() { + let p = self._get_pin(model_id, n)?; + s += &(p.action.as_char()).to_string(); } + Ok(s) } - pub fn get_physical_pin(&self, name: &str) -> Option<&Pin> { - if let Some(grp) = self.pins.get(name) { - if let Some(physical_pin) = self.physical_pins.get(&grp.pin_names[0]) { - return Option::Some(physical_pin); - } + pub fn get_pin_reset_actions(&self, model_id: usize, names: &Vec) -> Result { + let mut s = String::from(""); + for n in names.iter() { + let p = self._get_pin(model_id, n)?; + s += &(p.reset_action.unwrap_or(PinActions::HighZ).as_char()).to_string(); } - Option::None + Ok(s) } - pub fn get_mut_physical_pin(&mut self, name: &str) -> Option<&mut Pin> { - if let Some(grp) = self.pins.get(name) { - if let Some(physical_pin) = self.physical_pins.get_mut(&grp.pin_names[0]) { - return Option::Some(physical_pin); + pub fn set_pin_actions( + &mut self, + model_id: usize, + names: &Vec, + action: PinActions, + data: Option, + mask: Option, + ) -> Result<(), Error> { + if let Some(d) = data { + self.set_pin_data(model_id, names, d, mask)?; + } + + let mut m = (mask.unwrap_or(!(0 as usize))) as u32; + for (_i, n) in names.iter().rev().enumerate() { + let p = self._get_mut_pin(model_id, n)?; + + if m & 0x1 == 1 { + p.action = action; + } else { + p.action = PinActions::HighZ; } + m >>= 1; } - Option::None + Ok(()) } - pub fn _get_physical_pin(&self, name: &str) -> Result<&Pin, Error> { - match self.get_physical_pin(name) { - Some(p) => Ok(p), - None => Err(Error::new(&format!("Cannot find phyiscal pin '{}'!", name))), - } + pub fn drive_pins( + &mut self, + model_id: usize, + names: &Vec, + data: Option, + mask: Option, + ) -> Result<(), Error> { + self.set_pin_actions(model_id, names, PinActions::Drive, data, mask) } - pub fn _get_mut_physical_pin(&mut self, name: &str) -> Result<&mut Pin, Error> { - match self.get_mut_physical_pin(name) { - Some(p) => Ok(p), - None => Err(Error::new(&format!("Cannot find phyiscal pin '{}'!", name))), - } + pub fn verify_pins( + &mut self, + model_id: usize, + names: &Vec, + data: Option, + mask: Option, + ) -> Result<(), Error> { + self.set_pin_actions(model_id, names, PinActions::Verify, data, mask) } - pub fn contains(&self, name: &str) -> bool { - return self.get_pin_group(name).is_some(); + pub fn capture_pins(&mut self, model_id: usize, names: &Vec, mask: Option) -> Result<(), Error> { + self.set_pin_actions(model_id, names, PinActions::Capture, Option::None, mask) } - pub fn _contains(&self, name: &str) -> bool { - return self.get_physical_pin(name).is_some(); + pub fn highz_pins(&mut self, model_id: usize, names: &Vec, mask: Option) -> Result<(), Error> { + self.set_pin_actions(model_id, names, PinActions::HighZ, Option::None, mask) } /// Given a group/collection of pin names, verify: /// * Each pin exist /// * Each pin is unique (no duplicate pins) AND it points to a unique physical pin. That is, each pin is unique after resolving aliases. /// If all the above is met, we can group/collect these names. - pub fn verify_names(&self, names: &Vec) -> Result, Error> { + pub fn verify_names(&self, model_id: usize, names: &Vec) -> Result, Error> { let mut physical_names: Vec = vec![]; for (_i, pin_name) in names.iter().enumerate() { if pin_name.starts_with("/") && pin_name.ends_with("/") { @@ -296,8 +387,9 @@ impl Model { let regex = Regex::new(®ex_str).unwrap(); let mut _pin_names: Vec = vec![]; - for (name_str, grp) in self.pins.iter() { + for (name_str, grp_id) in self.models[model_id].pin_groups.iter() { if regex.is_match(name_str) { + let grp = &self.pin_groups[*grp_id]; for _name_str in grp.pin_names.iter() { if physical_names.contains(_name_str) { return Err(Error::new(&format!("Can not collect pin '{}' from regex /{}/ because it (or an alias of it) has already been collected (resolves to physical pin '{}')!", name_str, regex_str, _name_str))); @@ -308,13 +400,11 @@ impl Model { } _pin_names.sort(); physical_names.extend(_pin_names); - } else if let Some(p) = self.get_physical_pin(pin_name) { + } else if let Some(p) = self.resolve_to_physical_pin(model_id, pin_name) { if physical_names.contains(&p.name) { return Err(Error::new(&format!("Can not collect pin '{}' because it (or an alias of it) has already been collected (resolves to physical pin '{}')!", pin_name, p.name))); - } else { - //physical_names.push(String::from(&p.name)); } - if let Some(p) = self.get_pin_group(pin_name) { + if let Some(p) = self.get_pin_group(model_id, pin_name) { physical_names.extend_from_slice(&p.pin_names); } } else { @@ -330,33 +420,35 @@ impl Model { pub fn collect( &mut self, model_id: usize, - path: &str, names: Vec, endianness: Option, ) -> Result { - let pnames = self.verify_names(&names)?; - Ok(PinCollection::new(model_id, path, &pnames, endianness)) + let pnames = self.verify_names(model_id, &names)?; + Ok(PinCollection::new(model_id, &pnames, endianness)) } - /// Given a pin name, check if the pin or any of its aliases are present in pin group. - pub fn pin_group_contains(&mut self, name: &str, query_name: &str) -> Result { - let result = self.index_of(name, query_name)?.is_some(); + pub fn pin_names_contain( + &self, + model_id: usize, + names: &Vec, + query_name: &str, + ) -> Result { + let result = self.find_in_names(model_id, names, query_name)?.is_some(); Ok(result) } - /// Given a pin or alias name, finds either its name or alias in the group. - pub fn index_of(&self, name: &str, query_name: &str) -> Result, Error> { - if !self.pins.contains_key(name) { - // Pin group doesn't exists. Raise an error. - return Err(Error::new(&format!( - "Group {} does not exists! Cannot lookup index for {} in this group!", - name, query_name - ))); - } - - if let Some(p) = self.get_physical_pin(query_name) { - if let Some(idx) = p.groups.get(name) { - Ok(Option::Some(*idx)) + pub fn find_in_names( + &self, + model_id: usize, + names: &Vec, + query_name: &str, + ) -> Result, Error> { + if let Some(p) = self.get_pin(model_id, query_name) { + let idx = names + .iter() + .position(|name| p.name == *name || p.aliases.contains(name)); + if let Some(_idx) = idx { + Ok(Option::Some(_idx)) } else { // Group name wasn't found in this pin's groups. // Pin doesn't belong to that group. @@ -371,26 +463,19 @@ impl Model { } } - pub fn pin_names_contain( - &mut self, - names: &Vec, - query_name: &str, - ) -> Result { - let result = self.find_in_names(names, query_name)?.is_some(); - Ok(result) - } + /// Given a pin or alias name, finds either its name or alias in the group. + pub fn index_of(&self, model_id: usize, name: &str, query_name: &str) -> Result, Error> { + if !self.models[model_id].pin_groups.contains_key(name) { + // Pin group doesn't exists. Raise an error. + return Err(Error::new(&format!( + "Group {} does not exists! Cannot lookup index for {} in this group!", + name, query_name + ))); + } - pub fn find_in_names( - &self, - names: &Vec, - query_name: &str, - ) -> Result, Error> { - if let Some(p) = self.get_physical_pin(query_name) { - let idx = names - .iter() - .position(|name| p.name == *name || p.aliases.contains(name)); - if let Some(_idx) = idx { - Ok(Option::Some(_idx)) + if let Some(p) = self.get_pin(model_id, query_name) { + if let Some(idx) = p.groups.get(name) { + Ok(Option::Some(*idx)) } else { // Group name wasn't found in this pin's groups. // Pin doesn't belong to that group. @@ -405,6 +490,42 @@ impl Model { } } + pub fn resolve_to_physical_pin(&self, model_id: usize, name: &str) -> Option<&Pin> { + if let Some(grp) = self.get_pin_group(model_id, name) { + if let Some(physical_pin) = self.get_pin(model_id, &grp.pin_names[0]) { + return Option::Some(physical_pin); + } + } + Option::None + } + + pub fn resolve_to_mut_physical_pin(&mut self, model_id: usize, name: &str) -> Option<&mut Pin> { + let n; + match self.get_pin_group(model_id, name) { + Some(grp) => { + n = grp.pin_names[0].clone(); + }, + None => return Option::None, + } + self.get_mut_pin(model_id, &n) + } + + pub fn _resolve_to_physical_pin(&self, model_id: usize, name: &str) -> Result<&Pin, Error> { + match self.resolve_to_physical_pin(model_id, name) { + Some(p) => Ok(p), + None => Err(Error::new(&format!("Cannot find phyiscal pin '{}'!", name))), + } + } + + pub fn resolve_pin_names(&self, model_id: usize, names: &Vec) -> Result, Error> { + let mut physical_names: Vec = vec!(); + for (_i, n) in names.iter().enumerate() { + let p = self._resolve_to_physical_pin(model_id, n)?; + physical_names.push(p.name.clone()); + } + Ok(physical_names) + } + pub fn data_fits_in_pins(&mut self, pins: &Vec, data: u32) -> Result<(), Error> { let two: u32 = 2; if data > (two.pow(pins.len() as u32) - 1) { @@ -430,7 +551,7 @@ impl Model { } } - pub fn verify_action_string_fits(&self, width: u32, action_string: &[u8]) -> Result<(), Error> { + pub fn verify_action_string_fits(&self, width: u32, action_string: &Vec) -> Result<(), Error> { if action_string.len() != (width as usize) { Err(Error::new(&format!( "Action string of length {} must match width {}!", @@ -442,126 +563,17 @@ impl Model { } } - pub fn get_pin_data(&self, names: &Vec) -> u32 { - let mut data = 0; - for n in names.iter().rev() { - let p = self.get_physical_pin(n).unwrap(); - data = (data << 1) + p.data; - } - data as u32 - } - - pub fn get_pin_reset_data(&self, names: &Vec) -> u32 { - let mut rdata = 0; - for n in names.iter().rev() { - let p = self.get_physical_pin(n).unwrap(); - rdata = (rdata << 1) + p.reset_data.unwrap_or(0); - } - rdata as u32 - } - - pub fn reset_pin_names(&mut self, names: &Vec) -> Result<(), Error> { - for n in names.iter() { - let p = self.get_mut_physical_pin(n).unwrap(); - p.reset(); - } - Ok(()) - } - - pub fn set_pin_data( - &mut self, - names: &Vec, - data: u32, - mask: Option, - ) -> Result<(), Error> { - self.data_fits_in_pins(names, data)?; - - let mut d = data; - let mut m = (mask.unwrap_or(!(0 as usize))) as u32; - for n in names.iter() { - let p = self._pin(n).unwrap(); - p.set_data(((d & 0x1) & (m & 0x1)) as u8)?; - d = d >> 1; - m = m >> 1; - } - Ok(()) - } - - pub fn get_pin_actions(&mut self, names: &Vec) -> Result { - let mut s = String::from(""); - for n in names.iter() { - let p = self._pin(n).unwrap(); - s += &(p.action.as_char()).to_string(); - } - Ok(s) - } - - pub fn get_pin_reset_actions(&mut self, names: &Vec) -> Result { - let mut s = String::from(""); - for n in names.iter() { - let p = self._pin(n).unwrap(); - s += &(p.reset_action.unwrap_or(PinActions::HighZ).as_char()).to_string(); - } - Ok(s) - } - - pub fn set_pin_actions( - &mut self, - names: &Vec, - action: PinActions, - data: Option, - mask: Option, - ) -> Result<(), Error> { - if let Some(d) = data { - self.set_pin_data(names, d, mask)?; - } - - let mut m = (mask.unwrap_or(!(0 as usize))) as u32; - for (_i, n) in names.iter().rev().enumerate() { - let p = self._pin(n).unwrap(); - - if m & 0x1 == 1 { - p.action = action; - } else { - p.action = PinActions::HighZ; - } - m >>= 1; - } - Ok(()) - } - - pub fn resolve_pin_names(&mut self, names: &Vec) -> Result, Error> { - let mut physical_names: Vec = vec![]; - for (_i, n) in names.iter().enumerate() { - let p = self._pin(n).unwrap(); - physical_names.push(p.name.clone()); - } - Ok(physical_names) - } - - pub fn drive_pins( - &mut self, - names: &Vec, - data: Option, - mask: Option, - ) -> Result<(), Error> { - self.set_pin_actions(names, PinActions::Drive, data, mask) - } - - pub fn verify_pins( - &mut self, - names: &Vec, - data: Option, - mask: Option, - ) -> Result<(), Error> { - self.set_pin_actions(names, PinActions::Verify, data, mask) + /// Given a pin name, check if the pin or any of its aliases are present in pin group. + pub fn pin_group_contains(&self, model_id: usize, name: &str, query_name: &str) -> Result { + let result = self.index_of(model_id, name, query_name)?.is_some(); + Ok(result) } - pub fn capture_pins(&mut self, names: &Vec, mask: Option) -> Result<(), Error> { - self.set_pin_actions(names, PinActions::Capture, Option::None, mask) + pub fn contains(&self, model_id: usize, name: &str) -> bool { + return self.get_pin_group(model_id, name).is_some(); } - pub fn highz_pins(&mut self, names: &Vec, mask: Option) -> Result<(), Error> { - self.set_pin_actions(names, PinActions::HighZ, Option::None, mask) + pub fn _contains(&self, model_id: usize, name: &str) -> bool { + return self.get_pin(model_id, name).is_some(); } } diff --git a/rust/origen/src/core/model/pins/pin.rs b/rust/origen/src/core/model/pins/pin.rs index 0a847026..5cbeb542 100644 --- a/rust/origen/src/core/model/pins/pin.rs +++ b/rust/origen/src/core/model/pins/pin.rs @@ -71,7 +71,7 @@ impl TryFrom for PinActions { } /// Available Pin Roles -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum PinRoles { Standard, Power, @@ -81,12 +81,13 @@ pub enum PinRoles { } /// Model for single pin. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Pin { + pub model_id: usize, + pub id: usize, // Since pins will be added from the add_pin function of Pins, // just reuse that String instance instead of creating a new one. pub name: String, - pub path: String, pub data: u8, /// The pin's current action. If no action is desired, the pin will be HighZ. @@ -181,14 +182,16 @@ impl Pin { } pub fn new( + model_id: usize, + id: usize, name: String, - path: String, reset_data: Option, reset_action: Option, ) -> Pin { let mut p = Pin { + model_id: model_id, + id: id, name: name, - path: path, data: 0, action: PinActions::HighZ, reset_data: reset_data, @@ -206,8 +209,9 @@ impl Pin { impl Default for Pin { fn default() -> Pin { Self::new( + 0, + 0, String::from("default"), - String::from(""), Option::None, Option::None, ) diff --git a/rust/origen/src/core/model/pins/pin_collection.rs b/rust/origen/src/core/model/pins/pin_collection.rs index 9d1e6b75..34437aaf 100644 --- a/rust/origen/src/core/model/pins/pin_collection.rs +++ b/rust/origen/src/core/model/pins/pin_collection.rs @@ -1,14 +1,13 @@ use super::super::pins::Endianness; -use super::super::Model; use super::pin::PinActions; use crate::error::Error; +use super::super::super::dut::Dut; /// Model for a collection (or group) of pins #[derive(Debug, Clone)] pub struct PinCollection { pub pin_names: Vec, pub endianness: Endianness, - pub path: String, pub mask: Option, pub model_id: usize, } @@ -16,12 +15,10 @@ pub struct PinCollection { impl PinCollection { pub fn new( model_id: usize, - path: &str, pin_names: &Vec, endianness: Option, ) -> PinCollection { PinCollection { - path: path.to_string(), pin_names: match endianness { Some(e) => match e { Endianness::LittleEndian => pin_names.iter().map(|p| String::from(p)).collect(), @@ -61,7 +58,6 @@ impl PinCollection { } Ok(PinCollection::new( self.model_id, - &self.path, &sliced_names, Option::Some(self.endianness), )) @@ -79,7 +75,7 @@ impl PinCollection { } } -impl Model { +impl Dut { pub fn drive_pin_collection( &mut self, pin_collection: &mut PinCollection, @@ -119,30 +115,30 @@ impl Model { let pin_names = &collection.pin_names; let mask = collection.mask; collection.mask = Option::None; - self.set_pin_actions(pin_names, action, data, mask) + self.set_pin_actions(collection.model_id, pin_names, action, data, mask) } - pub fn get_pin_collection_data(&mut self, collection: &PinCollection) -> Result { - let pin_names = &collection.pin_names; - Ok(self.get_pin_data(&pin_names)) - } + // pub fn get_pin_collection_data(&mut self, collection: &PinCollection) -> Result { + // let pin_names = &collection.pin_names; + // Ok(self.get_pin_data(&pin_names)) + // } - pub fn get_pin_collection_reset_data(&mut self, collection: &PinCollection) -> u32 { + pub fn get_pin_collection_reset_data(&self, collection: &PinCollection) -> Result { let pin_names = &collection.pin_names; - self.get_pin_reset_data(&pin_names) + self.get_pin_reset_data(collection.model_id, &pin_names) } pub fn get_pin_collection_reset_actions( - &mut self, + &self, collection: &PinCollection, ) -> Result { let pin_names = &collection.pin_names; - self.get_pin_reset_actions(&pin_names) + self.get_pin_reset_actions(collection.model_id, &pin_names) } pub fn reset_pin_collection(&mut self, collection: &PinCollection) -> Result<(), Error> { let pin_names = &collection.pin_names; - self.reset_pin_names(&pin_names) + self.reset_pin_names(collection.model_id, &pin_names) } pub fn set_pin_collection_data( @@ -151,7 +147,7 @@ impl Model { data: u32, ) -> Result<(), Error> { let pin_names = &collection.pin_names; - self.set_pin_data(&pin_names, data, collection.mask) + self.set_pin_data(collection.model_id, &pin_names, data, collection.mask) } pub fn set_pin_collection_nonsticky_mask( diff --git a/rust/origen/src/core/model/pins/pin_group.rs b/rust/origen/src/core/model/pins/pin_group.rs index fd6f3e8f..e474cdcb 100644 --- a/rust/origen/src/core/model/pins/pin_group.rs +++ b/rust/origen/src/core/model/pins/pin_group.rs @@ -1,20 +1,16 @@ -//use super::pin::{PinActions}; -//use crate::error::Error; -//use super::super::super::super::DUT; use super::super::pins::Endianness; -use super::super::Model; use super::pin::PinActions; use super::pin_collection::PinCollection; use crate::error::Error; - -//use crate::core::model::Model; +use super::super::super::dut::Dut; // We'll maintain both the pin_names which the group was built with, but we'll also maintain the list // of physical names. Even though we can resolve this later, most operations wil #[derive(Debug, Clone)] pub struct PinGroup { + pub model_id: usize, + pub id: usize, pub name: String, - pub path: String, pub pin_names: Vec, pub endianness: Endianness, pub mask: Option, @@ -22,14 +18,16 @@ pub struct PinGroup { impl PinGroup { pub fn new( + model_id: usize, + id: usize, name: String, - path: String, pins: Vec, endianness: Option, ) -> PinGroup { return PinGroup { + model_id: model_id, + id: id, name: String::from(name), - path: String::from(path), pin_names: match endianness { Some(e) => match e { Endianness::LittleEndian => pins, @@ -65,99 +63,104 @@ impl PinGroup { } } -impl Model { - pub fn get_pin_group_data(&self, name: &str) -> u32 { - let pin_names = &self.get_pin_group(name).unwrap().pin_names; - self.get_pin_data(pin_names) +impl Dut { + pub fn get_pin_group_data(&self, model_id: usize, name: &str) -> Result { + let pin_names = &self._get_pin_group(model_id, name)?.pin_names; + self.get_pin_data(model_id, pin_names) } - pub fn get_pin_group_reset_data(&self, name: &str) -> u32 { - let pin_names = &self.get_pin_group(name).unwrap().pin_names; - self.get_pin_reset_data(&pin_names) + pub fn get_pin_group_reset_data(&self, model_id: usize, name: &str) -> Result { + let pin_names = &self._get_pin_group(model_id, name)?.pin_names; + self.get_pin_reset_data(model_id, &pin_names) } - pub fn reset_pin_group(&mut self, name: &str) -> Result<(), Error> { - let pin_names = self.get_pin_group(name).unwrap().pin_names.clone(); - self.reset_pin_names(&pin_names) + pub fn reset_pin_group(&mut self, model_id: usize, name: &str) -> Result<(), Error> { + let pin_names = self._get_pin_group(model_id, name)?.pin_names.clone(); + self.reset_pin_names(model_id, &pin_names) } - pub fn set_pin_group_data(&mut self, name: &str, data: u32) -> Result<(), Error> { - let grp = self.get_pin_group(name).unwrap(); + pub fn set_pin_group_data(&mut self, model_id: usize, name: &str, data: u32) -> Result<(), Error> { + let grp = self._get_pin_group(model_id, name)?; let m = grp.mask; let pin_names = grp.pin_names.clone(); - self.set_pin_data(&pin_names, data, m) + self.set_pin_data(model_id, &pin_names, data, m) } - pub fn resolve_pin_group_names(&mut self, name: &str) -> Result, Error> { - let pin_names = self.get_pin_group(name).unwrap().pin_names.clone(); - self.resolve_pin_names(&pin_names) + pub fn resolve_pin_group_names(&self, model_id: usize, name: &str) -> Result, Error> { + let pin_names = self._get_pin_group(model_id, name)?.pin_names.clone(); + self.resolve_pin_names(model_id, &pin_names) } /// Returns the pin actions as a string. /// E.g.: for an 8-pin bus where the two MSBits are driving, the next two are capturing, then next wo are verifying, and the /// two LSBits are HighZ, the return value will be "DDCCVVZZ" - pub fn get_pin_group_actions(&mut self, name: &str) -> Result { - let pin_names = self.get_pin_group(name).unwrap().pin_names.clone(); - self.get_pin_actions(&pin_names) + pub fn get_pin_group_actions(&self, model_id: usize, name: &str) -> Result { + let pin_names = self._get_pin_group(model_id, name)?.pin_names.clone(); + self.get_pin_actions(model_id, &pin_names) } - pub fn get_pin_group_reset_actions(&mut self, name: &str) -> Result { - let pin_names = self.get_pin_group(name).unwrap().pin_names.clone(); - self.get_pin_reset_actions(&pin_names) + pub fn get_pin_group_reset_actions(&self, model_id: usize, name: &str) -> Result { + let pin_names = self._get_pin_group(model_id, name)?.pin_names.clone(); + self.get_pin_reset_actions(model_id, &pin_names) } pub fn set_pin_group_actions( &mut self, + model_id: usize, name: &str, action: PinActions, data: Option, mask: Option, ) -> Result<(), Error> { - let pin_names = self.get_pin_group(name).unwrap().pin_names.clone(); - self.set_pin_actions(&pin_names, action, data, mask) + let pin_names = self._get_pin_group(model_id, name)?.pin_names.clone(); + self.set_pin_actions(model_id, &pin_names, action, data, mask) } pub fn drive_pin_group( &mut self, + model_id: usize, group_name: &str, data: Option, mask: Option, ) -> Result<(), Error> { - return self.set_pin_group_actions(group_name, PinActions::Drive, data, mask); + return self.set_pin_group_actions(model_id, group_name, PinActions::Drive, data, mask); } pub fn verify_pin_group( &mut self, + model_id: usize, group_name: &str, data: Option, mask: Option, ) -> Result<(), Error> { - return self.set_pin_group_actions(group_name, PinActions::Verify, data, mask); + return self.set_pin_group_actions(model_id, group_name, PinActions::Verify, data, mask); } pub fn capture_pin_group( &mut self, + model_id: usize, group_name: &str, mask: Option, ) -> Result<(), Error> { - return self.set_pin_group_actions(group_name, PinActions::Capture, Option::None, mask); + return self.set_pin_group_actions(model_id, group_name, PinActions::Capture, Option::None, mask); } - pub fn highz_pin_group(&mut self, group_name: &str, mask: Option) -> Result<(), Error> { - return self.set_pin_group_actions(group_name, PinActions::HighZ, Option::None, mask); + pub fn highz_pin_group(&mut self, model_id: usize, group_name: &str, mask: Option) -> Result<(), Error> { + return self.set_pin_group_actions(model_id, group_name, PinActions::HighZ, Option::None, mask); } // Assume the pin group is properly defined (that is, not pin duplicates and all pins exists. If the pin group exists, these should both be met) pub fn slice_pin_group( - &mut self, + &self, + model_id: usize, name: &str, start_idx: usize, stop_idx: usize, step_size: usize, ) -> Result { - if let Some(p) = self.get_pin_group(name) { + if let Some(p) = self.get_pin_group(model_id, name) { let names = &p.pin_names; - let mut sliced_names: Vec = vec![]; + let mut sliced_names: Vec = vec!(); for i in (start_idx..=stop_idx).step_by(step_size) { if i >= names.len() { @@ -172,8 +175,7 @@ impl Model { sliced_names.push(p); } Ok(PinCollection::new( - self.id, - &self.name, + model_id, &sliced_names, Option::None, )) @@ -185,9 +187,9 @@ impl Model { } } - pub fn set_pin_group_nonsticky_mask(&mut self, name: &str, mask: usize) -> Result<(), Error> { - let grp = self._get_mut_pin_group(name)?; - grp.mask = Some(mask); - Ok(()) - } +// pub fn set_pin_group_nonsticky_mask(&mut self, name: &str, mask: usize) -> Result<(), Error> { +// let grp = self._get_mut_pin_group(name)?; +// grp.mask = Some(mask); +// Ok(()) +// } } diff --git a/rust/pyapi/src/pins.rs b/rust/pyapi/src/pins.rs index c3156113..7faad6f0 100644 --- a/rust/pyapi/src/pins.rs +++ b/rust/pyapi/src/pins.rs @@ -35,9 +35,7 @@ pub fn pins(_py: Python, m: &PyModule) -> PyResult<()> { impl PyDUT { #[args(kwargs = "**")] fn add_pin(&self, model_id: usize, name: &str, kwargs: Option<&PyDict>) -> PyResult { - let path = ""; let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(model_id)?; let (mut reset_data, mut reset_action, mut width, mut offset, mut endianness): ( Option, Option, @@ -75,9 +73,9 @@ impl PyDUT { } None => {} } - model.add_pin( + dut.add_pin( + model_id, name, - path, width, offset, reset_data, @@ -87,13 +85,11 @@ impl PyDUT { let gil = Python::acquire_gil(); let py = gil.python(); - let p = model.get_pin_group(name); - match p { + match dut.get_pin_group(model_id, name) { Some(_p) => Ok(Py::new( py, PinGroup { name: String::from(name), - path: String::from(path), model_id: model_id, }, ) @@ -104,19 +100,15 @@ impl PyDUT { } fn pin(&self, model_id: usize, name: &str) -> PyResult { - let path = ""; - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(model_id)?; + let dut = DUT.lock().unwrap(); let gil = Python::acquire_gil(); let py = gil.python(); - let p = model.get_pin_group(name); - match p { + match dut.get_pin_group(model_id, name) { Some(_p) => Ok(Py::new( py, PinGroup { name: String::from(name), - path: String::from(path), model_id: model_id, }, ) @@ -129,27 +121,19 @@ impl PyDUT { #[args(aliases = "*")] fn add_pin_alias(&self, model_id: usize, name: &str, aliases: &PyTuple) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(model_id)?; - for alias in aliases { let _alias: String = alias.extract()?; - model.add_pin_alias(name, &_alias)?; + dut.add_pin_alias(model_id, name, &_alias)?; } Ok(()) } fn pins(&self, model_id: usize) -> PyResult> { - let path = ""; - // Even though we won't use the model, make sure the DUT exists and the model_id is reachable. - let mut dut = DUT.lock().unwrap(); - let _model = dut.get_mut_model(model_id)?; - let gil = Python::acquire_gil(); let py = gil.python(); Ok(Py::new( py, PinContainer { - path: String::from(path), model_id: model_id, }, ) @@ -164,8 +148,6 @@ impl PyDUT { pins: &PyTuple, options: Option<&PyDict>, ) -> PyResult { - let path = ""; - let mut dut = DUT.lock().unwrap(); let mut endianness = Option::None; match options { Some(opts) => { @@ -179,39 +161,28 @@ impl PyDUT { } None => {} } - let model = dut.get_mut_model(model_id)?; - model.group_pins(name, path, pins.extract()?, endianness)?; + let mut dut = DUT.lock().unwrap(); + dut.group_pins_by_name(model_id, name, pins.extract()?, endianness)?; let gil = Python::acquire_gil(); let py = gil.python(); - let p = model.get_pin_group(name); - match p { - Some(_p) => Ok(Py::new( - py, - PinGroup { - name: String::from(name), - path: String::from(path), - model_id: model_id, - }, - ) - .unwrap() - .to_object(py)), - None => Ok(py.None()), - } + Ok(Py::new( + py, + PinGroup { + name: String::from(name), + model_id: model_id, + }, + ) + .unwrap() + .to_object(py)) } fn physical_pins(&self, model_id: usize) -> PyResult> { - let path = ""; - // Even though we won't use the model, make sure the DUT exists and the model_id is reachable. - let mut dut = DUT.lock().unwrap(); - let _model = dut.get_mut_model(model_id)?; - let gil = Python::acquire_gil(); let py = gil.python(); Ok(Py::new( py, PhysicalPinContainer { - path: String::from(path), model_id: model_id, }, ) @@ -219,19 +190,15 @@ impl PyDUT { } fn physical_pin(&self, model_id: usize, name: &str) -> PyResult { - let path = ""; - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(model_id)?; + let dut = DUT.lock().unwrap(); let gil = Python::acquire_gil(); let py = gil.python(); - let p = model.get_physical_pin(name); - match p { + match dut.get_pin(model_id, name) { Some(_p) => Ok(Py::new( py, Pin { name: String::from(name), - path: String::from(path), model_id: model_id, }, ) diff --git a/rust/pyapi/src/pins/physical_pin_container.rs b/rust/pyapi/src/pins/physical_pin_container.rs index c8e7484c..a06245a2 100644 --- a/rust/pyapi/src/pins/physical_pin_container.rs +++ b/rust/pyapi/src/pins/physical_pin_container.rs @@ -1,78 +1,31 @@ -use super::pin::Pin; -use origen::DUT; use pyo3::class::mapping::*; -use pyo3::exceptions; use pyo3::prelude::*; #[allow(unused_imports)] use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PyTuple}; +use indexmap::map::IndexMap; +use crate::meta::py_like_apis::dict_like_api::{DictLikeAPI, DictLikeIter}; #[pyclass] pub struct PhysicalPinContainer { - pub path: String, pub model_id: usize, } #[pymethods] impl PhysicalPinContainer { fn keys(&self) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let names = &model.physical_pins; - - let mut v: Vec = Vec::new(); - for (n, _p) in names { - v.push(n.clone()); - } - Ok(v) + DictLikeAPI::keys(self) } - fn values(&self) -> PyResult>> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let physical_pins = &model.physical_pins; - - let gil = Python::acquire_gil(); - let py = gil.python(); - let mut v: Vec> = Vec::new(); - for (n, _p) in physical_pins { - v.push( - Py::new( - py, - Pin { - name: String::from(n.clone()), - path: String::from(self.path.clone()), - model_id: self.model_id, - }, - ) - .unwrap(), - ) - } - Ok(v) + fn values(&self) -> PyResult> { + DictLikeAPI::values(self) } - fn items(&self) -> PyResult)>> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pins = &model.physical_pins; + fn items(&self) -> PyResult> { + DictLikeAPI::items(self) + } - let gil = Python::acquire_gil(); - let py = gil.python(); - let mut items: Vec<(String, Py)> = Vec::new(); - for (n, _p) in pins { - items.push(( - n.clone(), - Py::new( - py, - Pin { - name: String::from(n.clone()), - path: String::from(self.path.clone()), - model_id: self.model_id, - }, - ) - .unwrap(), - )); - } - Ok(items) + fn get(&self, name: &str) -> PyResult { + DictLikeAPI::get(self, name) } #[getter] @@ -81,86 +34,42 @@ impl PhysicalPinContainer { } } -#[pyproto] -impl PyMappingProtocol for PhysicalPinContainer { - fn __getitem__(&self, name: &str) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let gil = Python::acquire_gil(); - let py = gil.python(); - let p = model.get_physical_pin(name); - match p { - Some(_p) => Ok(Py::new( - py, - Pin { - name: String::from(name), - path: String::from(&self.path), - model_id: self.model_id, - }, - ) - .unwrap()), - // Stay in sync with Python's Hash - Raise a KeyError if no pin is found. - None => Err(exceptions::KeyError::py_err(format!( - "No pin or pin alias found for {}", - name - ))), - } +impl DictLikeAPI for PhysicalPinContainer { + fn lookup_key(&self) -> &str { + &"pins" } - fn __len__(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.physical_pins.len()) + fn lookup_table( + &self, + dut: &std::sync::MutexGuard, + ) -> IndexMap { + dut.get_model(self.model_id).unwrap().pins.clone() } -} -#[pyproto] -impl pyo3::class::iter::PyIterProtocol for PhysicalPinContainer { - fn __iter__(slf: PyRefMut) -> PyResult { - let dut = DUT.lock().unwrap(); - let model = dut.get_model(slf.model_id)?; - Ok(PhysicalPinContainerIter { - keys: model.physical_pins.iter().map(|(s, _)| s.clone()).collect(), - i: 0, - }) + fn model_id(&self) -> usize { + self.model_id + } + + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { + Ok(Py::new(py, super::pin::Pin {model_id: model_id, name: name.to_string()}).unwrap().to_object(py)) } } #[pyproto] -impl pyo3::class::sequence::PySequenceProtocol for PhysicalPinContainer { - fn __contains__(&self, item: &str) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model._contains(item)) +impl PyMappingProtocol for PhysicalPinContainer { + fn __getitem__(&self, name: &str) -> PyResult { + DictLikeAPI::__getitem__(self, name) } -} -#[pyclass] -pub struct PhysicalPinContainerIter { - keys: Vec, - i: usize, + fn __len__(&self) -> PyResult { + DictLikeAPI::__len__(self) + } } #[pyproto] -impl pyo3::class::iter::PyIterProtocol for PhysicalPinContainerIter { - fn __iter__(slf: PyRefMut) -> PyResult { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(slf.to_object(py)) - } - - /// The Iterator will be created with an index starting at 0 and the pin names at the time of its creation. - /// For each call to 'next', we'll create a pin object with the next value in the list, or None, if no more keys are available. - /// Note: this means that the iterator can become stale if the PinContainer is changed. This can happen if the iterator is stored from Python code - /// directly. E.g.: i = dut.pins.__iter__() => iterator with the pin names at the time of creation, - /// Todo: Fix the above using iterators. My Rust skills aren't there yet though... - Coreyeng - fn __next__(mut slf: PyRefMut) -> PyResult> { - if slf.i >= slf.keys.len() { - return Ok(None); - } - let name = slf.keys[slf.i].clone(); - slf.i += 1; - Ok(Some(name)) +impl pyo3::class::iter::PyIterProtocol for PhysicalPinContainer { + fn __iter__(slf: PyRefMut) -> PyResult { + DictLikeAPI::__iter__(&*slf) } } diff --git a/rust/pyapi/src/pins/pin.rs b/rust/pyapi/src/pins/pin.rs index 2d632845..30efb42e 100644 --- a/rust/pyapi/src/pins/pin.rs +++ b/rust/pyapi/src/pins/pin.rs @@ -7,31 +7,29 @@ use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PyTuple}; #[pyclass] pub struct Pin { pub name: String, - pub path: String, pub model_id: usize, } -#[macro_export] -macro_rules! pypin { - ($py:expr, $name:expr, $model_id:expr) => { - Py::new( - $py, - crate::pins::pin::Pin { - name: String::from($name), - path: String::from(""), - model_id: model_id, - }, - ) - .unwrap() - }; -} +// #[macro_export] +// macro_rules! pypin { +// ($py:expr, $name:expr, $model_id:expr) => { +// Py::new( +// $py, +// crate::pins::pin::Pin { +// name: String::from($name), +// path: String::from(""), +// model_id: model_id, +// }, +// ) +// .unwrap() +// }; +// } #[pymethods] impl Pin { fn add_metadata(&self, id_str: &str, obj: &PyAny) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let pin = dut._get_mut_pin(self.model_id, &self.name)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -49,8 +47,7 @@ impl Pin { fn set_metadata(&self, id_str: &str, obj: &PyAny) -> PyResult { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let pin = dut._get_mut_pin(self.model_id, &self.name)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -75,9 +72,8 @@ impl Pin { } fn get_metadata(&self, id_str: &str) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; let gil = Python::acquire_gil(); let py = gil.python(); match pin.metadata.get(id_str) { @@ -99,50 +95,44 @@ impl Pin { #[getter] fn get_added_metadata(&self) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; Ok(pin.metadata.iter().map(|(k, _)| k.clone()).collect()) } // Even though we're storing the name in this instance, we're going to go back to the core anyway. #[getter] fn get_name(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; - Ok(pin.name.clone()) + let dut = DUT.lock().unwrap(); + let p = dut._get_pin(self.model_id, &self.name)?; + Ok(p.name.clone()) } #[getter] fn get_data(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; Ok(pin.data) } #[getter] fn get_action(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; Ok(String::from(pin.action.as_str())) } #[getter] fn get_aliases(&self) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; Ok(pin.aliases.clone()) } #[getter] fn get_reset_data(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._get_physical_pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -154,9 +144,8 @@ impl Pin { #[getter] fn get_reset_action(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pin = model._get_physical_pin(&self.name)?; + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -165,18 +154,4 @@ impl Pin { None => Ok(py.None()), } } - - // Debug helper: Get the name held by this instance. - #[allow(non_snake_case)] - #[getter] - fn get__name(&self) -> PyResult { - Ok(self.name.clone()) - } - - // Debug helper: Get the path held by this instance. - #[allow(non_snake_case)] - #[getter] - fn get__path(&self) -> PyResult { - Ok(self.path.clone()) - } } diff --git a/rust/pyapi/src/pins/pin_collection.rs b/rust/pyapi/src/pins/pin_collection.rs index 86d3d591..67e0309f 100644 --- a/rust/pyapi/src/pins/pin_collection.rs +++ b/rust/pyapi/src/pins/pin_collection.rs @@ -8,7 +8,6 @@ use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PySlice, PyTuple}; #[pyclass] pub struct PinCollection { - path: String, model_id: usize, pin_collection: OrigenPinCollection, } @@ -20,10 +19,9 @@ impl PinCollection { endianness: Option, ) -> Result { let mut dut = lock!()?; - let model = dut.get_mut_model(model_id)?; - let collection = model.collect(model_id, "", names, endianness)?; + //let model = dut.get_mut_model(model_id)?; + let collection = dut.collect(model_id, names, endianness)?; Ok(PinCollection { - path: String::from(""), pin_collection: collection, model_id: model_id, }) @@ -34,16 +32,14 @@ impl PinCollection { impl PinCollection { #[getter] fn get_data(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.pin_collection.model_id)?; - Ok(model.get_pin_data(&self.pin_collection.pin_names)) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_data(self.pin_collection.model_id, &self.pin_collection.pin_names)?) } #[setter] fn set_data(&self, data: u32) -> PyResult> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.set_pin_collection_data(&self.pin_collection, data)?; + dut.set_pin_collection_data(&self.pin_collection, data)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -53,7 +49,6 @@ impl PinCollection { Ok(Py::new( py, PinCollection { - path: self.path.clone(), pin_collection: self.pin_collection.clone(), model_id: self.model_id, }, @@ -63,15 +58,13 @@ impl PinCollection { fn with_mask(&mut self, mask: usize) -> PyResult> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.set_pin_collection_nonsticky_mask(&mut self.pin_collection, mask)?; + dut.set_pin_collection_nonsticky_mask(&mut self.pin_collection, mask)?; let gil = Python::acquire_gil(); let py = gil.python(); Ok(Py::new( py, PinCollection { - path: self.path.clone(), pin_collection: self.pin_collection.clone(), model_id: self.model_id, }, @@ -85,43 +78,37 @@ impl PinCollection { #[getter] fn get_pin_actions(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_actions(&self.pin_collection.pin_names)?) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_actions(self.model_id, &self.pin_collection.pin_names)?) } fn drive(&mut self, data: Option) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.drive_pin_collection(&mut self.pin_collection, data)?; + dut.drive_pin_collection(&mut self.pin_collection, data)?; Ok(()) } fn verify(&mut self, data: Option) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.verify_pin_collection(&mut self.pin_collection, data)?; + dut.verify_pin_collection(&mut self.pin_collection, data)?; Ok(()) } fn capture(&mut self) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.capture_pin_collection(&mut self.pin_collection)?; + dut.capture_pin_collection(&mut self.pin_collection)?; Ok(()) } fn highz(&mut self) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.highz_pin_collection(&mut self.pin_collection)?; + dut.highz_pin_collection(&mut self.pin_collection)?; Ok(()) } fn reset(&mut self) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.reset_pin_collection(&mut self.pin_collection)?; + dut.reset_pin_collection(&mut self.pin_collection)?; Ok(()) } @@ -137,23 +124,21 @@ impl PinCollection { #[getter] fn get_reset_data(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_collection_reset_data(&self.pin_collection)) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_collection_reset_data(&self.pin_collection)?) } #[getter] fn get_reset_actions(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_collection_reset_actions(&self.pin_collection)?) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_collection_reset_actions(&self.pin_collection)?) } - #[allow(non_snake_case)] - #[getter] - fn get__path(&self) -> PyResult { - Ok(self.pin_collection.path.clone()) - } + // #[allow(non_snake_case)] + // #[getter] + // fn get__path(&self) -> PyResult { + // Ok(self.pin_collection.path.clone()) + // } #[getter] fn get_big_endian(&self) -> PyResult { @@ -173,9 +158,8 @@ impl pyo3::class::sequence::PySequenceProtocol for PinCollection { } fn __contains__(&self, item: &str) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.pin_names_contain(&self.pin_collection.pin_names, item)?) + let dut = DUT.lock().unwrap(); + Ok(dut.pin_names_contain(self.model_id, &self.pin_collection.pin_names, item)?) } } @@ -220,7 +204,6 @@ impl pyo3::class::iter::PyIterProtocol for PinCollection { impl From for PinCollection { fn from(collection: OrigenPinCollection) -> Self { PinCollection { - path: collection.path.clone(), model_id: collection.model_id.clone(), pin_collection: collection, } diff --git a/rust/pyapi/src/pins/pin_container.rs b/rust/pyapi/src/pins/pin_container.rs index 1ecbdbf7..b57ecc9e 100644 --- a/rust/pyapi/src/pins/pin_container.rs +++ b/rust/pyapi/src/pins/pin_container.rs @@ -1,81 +1,34 @@ -use origen::DUT; use pyo3::class::mapping::*; -use pyo3::exceptions; use pyo3::prelude::*; #[allow(unused_imports)] use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PyTuple}; +use indexmap::map::IndexMap; +use crate::meta::py_like_apis::dict_like_api::{DictLikeAPI, DictLikeIter}; use super::pin_collection::PinCollection; -use super::pin_group::PinGroup; use origen::core::model::pins::Endianness; #[pyclass] pub struct PinContainer { - pub path: String, pub model_id: usize, } #[pymethods] impl PinContainer { fn keys(&self) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let names = &model.pins; - - let mut v: Vec = Vec::new(); - for (n, _p) in names { - v.push(n.clone()); - } - Ok(v) + DictLikeAPI::keys(self) } - fn values(&self) -> PyResult>> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pins = &model.pins; - - let gil = Python::acquire_gil(); - let py = gil.python(); - let mut v: Vec> = Vec::new(); - for (n, _p) in pins { - v.push( - Py::new( - py, - PinGroup { - name: String::from(n.clone()), - path: String::from(self.path.clone()), - model_id: self.model_id, - }, - ) - .unwrap(), - ) - } - Ok(v) + fn values(&self) -> PyResult> { + DictLikeAPI::values(self) } - fn items(&self) -> PyResult)>> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let pins = &model.pins; + fn items(&self) -> PyResult> { + DictLikeAPI::items(self) + } - let gil = Python::acquire_gil(); - let py = gil.python(); - let mut items: Vec<(String, Py)> = Vec::new(); - for (n, _p) in pins { - items.push(( - n.clone(), - Py::new( - py, - PinGroup { - name: String::from(n.clone()), - path: String::from(self.path.clone()), - model_id: self.model_id, - }, - ) - .unwrap(), - )); - } - Ok(items) + fn get(&self, name: &str) -> PyResult { + DictLikeAPI::get(self, name) } #[getter] @@ -117,86 +70,41 @@ impl PinContainer { } } -#[pyproto] -impl PyMappingProtocol for PinContainer { - fn __getitem__(&self, name: &str) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; +impl DictLikeAPI for PinContainer { + fn lookup_key(&self) -> &str { + &"pin_groups" + } - let gil = Python::acquire_gil(); - let py = gil.python(); - let p = model.get_pin_group(name); - match p { - Some(_p) => Ok(Py::new( - py, - PinGroup { - name: String::from(name), - path: String::from(&self.path), - model_id: self.model_id, - }, - ) - .unwrap()), - // Stay in sync with Python's Hash - Raise a KeyError if no pin is found. - None => Err(exceptions::KeyError::py_err(format!( - "No pin or pin alias found for {}", - name - ))), - } + fn lookup_table( + &self, + dut: &std::sync::MutexGuard, + ) -> IndexMap { + dut.get_model(self.model_id).unwrap().pin_groups.clone() } - fn __len__(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.pins.len()) + fn model_id(&self) -> usize { + self.model_id } -} -#[pyproto] -impl pyo3::class::iter::PyIterProtocol for PinContainer { - fn __iter__(slf: PyRefMut) -> PyResult { - let dut = DUT.lock().unwrap(); - let model = dut.get_model(slf.model_id)?; - Ok(PinContainerIter { - keys: model.pins.iter().map(|(s, _)| s.clone()).collect(), - i: 0, - }) + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { + Ok(Py::new(py, super::pin_group::PinGroup {model_id: model_id, name: name.to_string()}).unwrap().to_object(py)) } } #[pyproto] -impl pyo3::class::sequence::PySequenceProtocol for PinContainer { - fn __contains__(&self, item: &str) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.contains(item)) +impl PyMappingProtocol for PinContainer { + fn __getitem__(&self, name: &str) -> PyResult { + DictLikeAPI::__getitem__(self, name) } -} -#[pyclass] -pub struct PinContainerIter { - keys: Vec, - i: usize, + fn __len__(&self) -> PyResult { + DictLikeAPI::__len__(self) + } } #[pyproto] -impl pyo3::class::iter::PyIterProtocol for PinContainerIter { - fn __iter__(slf: PyRefMut) -> PyResult { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(slf.to_object(py)) - } - - /// The Iterator will be created with an index starting at 0 and the pin names at the time of its creation. - /// For each call to 'next', we'll create a pin object with the next value in the list, or None, if no more keys are available. - /// Note: this means that the iterator can become stale if the PinContainer is changed. This can happen if the iterator is stored from Python code - /// directly. E.g.: i = dut.pins.__iter__() => iterator with the pin names at the time of creation, - /// Todo: Fix the above using iterators. My Rust skills aren't there yet though... - Coreyeng - fn __next__(mut slf: PyRefMut) -> PyResult> { - if slf.i >= slf.keys.len() { - return Ok(None); - } - let name = slf.keys[slf.i].clone(); - slf.i += 1; - Ok(Some(name)) +impl pyo3::class::iter::PyIterProtocol for PinContainer { + fn __iter__(slf: PyRefMut) -> PyResult { + DictLikeAPI::__iter__(&*slf) } } diff --git a/rust/pyapi/src/pins/pin_group.rs b/rust/pyapi/src/pins/pin_group.rs index fe500262..df715083 100644 --- a/rust/pyapi/src/pins/pin_group.rs +++ b/rust/pyapi/src/pins/pin_group.rs @@ -1,4 +1,3 @@ -use super::pin::Pin; use super::pin_collection::PinCollection; use origen::DUT; use pyo3::prelude::*; @@ -8,7 +7,6 @@ use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PySlice, PyTuple}; #[pyclass] pub struct PinGroup { pub name: String, - pub path: String, pub model_id: usize, } @@ -17,24 +15,21 @@ impl PinGroup { // Even though we're storing the name in this instance, we're going to go back to the core anyway. #[getter] fn get_name(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let grp = model._get_pin_group(&self.name)?; + let dut = DUT.lock().unwrap(); + let grp = dut._get_pin_group(self.model_id, &self.name)?; Ok(grp.name.clone()) } #[getter] fn get_data(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_group_data(&self.name)) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_group_data(self.model_id, &self.name)?) } #[setter] fn set_data(&self, data: u32) -> PyResult> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.set_pin_group_data(&self.name, data)?; + dut.set_pin_group_data(self.model_id, &self.name, data)?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -42,7 +37,6 @@ impl PinGroup { py, Self { name: self.name.clone(), - path: self.path.clone(), model_id: self.model_id, }, ) @@ -53,29 +47,28 @@ impl PinGroup { return self.set_data(data); } - fn with_mask(&self, mask: usize) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - model.set_pin_group_nonsticky_mask(&self.name, mask)?; - - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(Py::new( - py, - Self { - name: self.name.clone(), - path: self.path.clone(), - model_id: self.model_id, - }, - ) - .unwrap()) - } + // fn with_mask(&self, mask: usize) -> PyResult> { + // let mut dut = DUT.lock().unwrap(); + // let model = dut.get_mut_model(self.model_id)?; + // model.set_pin_group_nonsticky_mask(&self.name, mask)?; + + // let gil = Python::acquire_gil(); + // let py = gil.python(); + // Ok(Py::new( + // py, + // Self { + // name: self.name.clone(), + // path: self.path.clone(), + // model_id: self.model_id, + // }, + // ) + // .unwrap()) + // } #[getter] fn get_pin_names(&self) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let grp = model._get_pin_group(&self.name)?; + let dut = DUT.lock().unwrap(); + let grp = dut._get_pin_group(self.model_id, &self.name)?; let mut v: Vec = Vec::new(); for n in grp.pin_names.iter() { @@ -84,110 +77,86 @@ impl PinGroup { Ok(v) } - #[getter] - fn get_pins(&self) -> PyResult>> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let grp = model._get_pin_group(&self.name)?; - - let gil = Python::acquire_gil(); - let py = gil.python(); - let mut v: Vec> = Vec::new(); - for n in grp.pin_names.iter() { - v.push( - Py::new( - py, - Pin { - name: String::from(n), - path: String::from(&self.path), - model_id: self.model_id, - }, - ) - .unwrap(), - ); - } - Ok(v) - } + // #[getter] + // fn get_pins(&self) -> PyResult>> { + // let mut dut = DUT.lock().unwrap(); + // let model = dut.get_mut_model(self.model_id)?; + // let grp = model._get_pin_group(&self.name)?; + + // let gil = Python::acquire_gil(); + // let py = gil.python(); + // let mut v: Vec> = Vec::new(); + // for n in grp.pin_names.iter() { + // v.push( + // Py::new( + // py, + // Pin { + // name: String::from(n), + // path: String::from(&self.path), + // model_id: self.model_id, + // }, + // ) + // .unwrap(), + // ); + // } + // Ok(v) + // } #[getter] fn get_pin_actions(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_group_actions(&self.name)?) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_group_actions(self.model_id, &self.name)?) } fn drive(&self, data: Option) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.drive_pin_group(&self.name, data, Option::None)?) + Ok(dut.drive_pin_group(self.model_id, &self.name, data, Option::None)?) } fn verify(&self, data: Option) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.verify_pin_group(&self.name, data, Option::None)?) + Ok(dut.verify_pin_group(self.model_id, &self.name, data, Option::None)?) } fn capture(&self) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.capture_pin_group(&self.name, Option::None)?) + Ok(dut.capture_pin_group(self.model_id, &self.name, Option::None)?) } fn highz(&self) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.highz_pin_group(&self.name, Option::None)?) + Ok(dut.highz_pin_group(self.model_id, &self.name, Option::None)?) } fn reset(&self) -> PyResult<()> { let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.reset_pin_group(&self.name)?) + Ok(dut.reset_pin_group(self.model_id, &self.name)?) } #[getter] fn get_physical_names(&self) -> PyResult> { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let names = model.resolve_pin_group_names(&self.name)?; + let dut = DUT.lock().unwrap(); + let names = dut.resolve_pin_group_names(self.model_id, &self.name)?; Ok(names.clone()) } #[getter] fn get_width(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let grp = model._get_pin_group(&self.name)?; + let dut = DUT.lock().unwrap(); + let grp = dut._get_pin_group(self.model_id, &self.name)?; Ok(grp.len()) } #[getter] fn get_reset_data(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_group_reset_data(&self.name)) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_group_reset_data(self.model_id, &self.name)?) } #[getter] fn get_reset_actions(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.get_pin_group_reset_actions(&self.name)?) - } - - // Debug helper: Get the name held by this instance. - #[allow(non_snake_case)] - #[getter] - fn get__name(&self) -> PyResult { - Ok(self.name.clone()) - } - - // Debug helper: Get the name held by this instance. - #[allow(non_snake_case)] - #[getter] - fn get__path(&self) -> PyResult { - Ok(self.path.clone()) + let dut = DUT.lock().unwrap(); + Ok(dut.get_pin_group_reset_actions(self.model_id, &self.name)?) } #[getter] @@ -198,9 +167,8 @@ impl PinGroup { #[getter] fn get_little_endian(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let grp = model._get_pin_group(&self.name)?; + let dut = DUT.lock().unwrap(); + let grp = dut._get_pin_group(self.model_id, &self.name)?; Ok(grp.is_little_endian()) } } @@ -208,16 +176,14 @@ impl PinGroup { #[pyproto] impl pyo3::class::sequence::PySequenceProtocol for PinGroup { fn __len__(&self) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - let grp = model._get_pin_group(&self.name)?; + let dut = DUT.lock().unwrap(); + let grp = dut._get_pin_group(self.model_id, &self.name)?; Ok(grp.len()) } fn __contains__(&self, item: &str) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; - Ok(model.pin_group_contains(&self.name, item)?) + let dut = DUT.lock().unwrap(); + Ok(dut.pin_group_contains(self.model_id, &self.name, item)?) } } @@ -225,15 +191,15 @@ impl pyo3::class::sequence::PySequenceProtocol for PinGroup { impl<'p> pyo3::class::PyMappingProtocol<'p> for PinGroup { // Indexing example: https://github.com/PyO3/pyo3/blob/master/tests/test_dunder.rs#L423-L438 fn __getitem__(&self, idx: &PyAny) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(self.model_id)?; + let dut = DUT.lock().unwrap(); let gil = Python::acquire_gil(); let py = gil.python(); if let Ok(slice) = idx.cast_as::() { // Indices requires (what I think is) a max size. Should be plenty. let indices = slice.indices(8192)?; - let collection = model.slice_pin_group( + let collection = dut.slice_pin_group( + self.model_id, &self.name, indices.start as usize, indices.stop as usize, @@ -244,7 +210,7 @@ impl<'p> pyo3::class::PyMappingProtocol<'p> for PinGroup { .to_object(py)) } else { let i = idx.extract::().unwrap(); - let collection = model.slice_pin_group(&self.name, i as usize, i as usize, 1)?; + let collection = dut.slice_pin_group(self.model_id, &self.name, i as usize, i as usize, 1)?; Ok(Py::new(py, PinCollection::from(collection)) .unwrap() .to_object(py)) @@ -255,9 +221,8 @@ impl<'p> pyo3::class::PyMappingProtocol<'p> for PinGroup { #[pyproto] impl pyo3::class::iter::PyIterProtocol for PinGroup { fn __iter__(slf: PyRefMut) -> PyResult { - let mut dut = DUT.lock().unwrap(); - let model = dut.get_mut_model(slf.model_id)?; - let grp = model._get_pin_group(&slf.name)?; + let dut = DUT.lock().unwrap(); + let grp = dut._get_pin_group(slf.model_id, &slf.name)?; Ok(PinGroupIter { keys: grp.pin_names.clone(), From 7f2a189437cc1d778be2566b4809bc2062ce9878 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Thu, 20 Feb 2020 17:53:24 -0600 Subject: [PATCH 05/18] (Sorry for pushing to an unrelated PR, but its easier this way) Part 1 of cleaning up the pin specs. Instead of one giant test script, actually broke most of them up into independent test cases. The ones that aren't (list-like test and meta), I'm planning to make interfaces and test drivers for, so left them alone for now. That'll be part 2 --- example/tests/pin_api_test.py | 1393 ++++++++++++++-------------- rust/origen/src/core/model/pins.rs | 27 +- rust/pyapi/src/pins.rs | 12 +- rust/pyapi/src/pins/pin.rs | 7 + 4 files changed, 722 insertions(+), 717 deletions(-) diff --git a/example/tests/pin_api_test.py b/example/tests/pin_api_test.py index 949fa247..b0791b8f 100644 --- a/example/tests/pin_api_test.py +++ b/example/tests/pin_api_test.py @@ -1,6 +1,7 @@ -import origen # pylint: disable=import-error -import _origen # pylint: disable=import-error import pytest +import origen, _origen # pylint: disable=import-error +from tests.shared import clean_eagle, clean_falcon, clean_tester # pylint: disable=import-error +from tests.shared.python_like_apis import Fixture_DictLikeAPI # pylint: disable=import-error class MyRandomClass: pass @@ -19,195 +20,698 @@ def check_alias(pin_name, alias_name): assert origen.dut.pins[alias_name].pin_names == [pin_name] assert alias_name in origen.dut.physical_pin(pin_name).aliases +# Most basic operations will be hit just by the DictLikeAPIs passing. +# That is, if those pass then we already know that adding/retrieving a single pin, +# length, and contains already work. So, focus the remaining tests on aspects +# not covered by the API testers. + +class TestPinContainerDictLike(Fixture_DictLikeAPI): + def parameterize(self): + return { + "keys": ["p0", "p1", "p2", "p3"], + "klass": _origen.dut.pins.PinGroup, + "not_in_dut": "p4" + } + + def boot_dict_under_test(self): + origen.app.instantiate_dut("dut.falcon") + dut = origen.dut + dut.add_pin("p0") + dut.add_pin("p1") + dut.add_pin("p2") + dut.add_pin("p3") + return dut.pins + +class TestPhysicalPinContainerDictLike(Fixture_DictLikeAPI): + def parameterize(self): + return { + "keys": ["p0", "p1", "p2", "p3"], + "klass": _origen.dut.pins.Pin, + "not_in_dut": "p4" + } + + def boot_dict_under_test(self): + origen.app.instantiate_dut("dut.falcon") + dut = origen.dut + dut.add_pin("p0") + dut.add_pin("p1") + dut.add_pin("p2") + dut.add_pin("p3") + return dut.physical_pins + +@pytest.fixture +def ports(): + origen.dut.add_pin("porta", width=4) + origen.dut.add_pin("portb", width=2) + +@pytest.fixture def pins(): - origen.dut.pins - -def p1(): - origen.dut.pin("p1") + origen.dut.add_pin("p0") + origen.dut.add_pin("p1") + origen.dut.add_pin("p2") + origen.dut.add_pin("p3") +@pytest.fixture def grp(): - origen.dut.pin("grp") - -def test_pin_api_boots(): - origen.app.instantiate_dut("dut.falcon") - # Ensure the DUT was set and the pin container is available. - assert origen.dut - assert isinstance(origen.dut.pins, _origen.dut.pins.PinContainer) - -def test_empty_pins(): - assert len(origen.dut.pins) == 0 - assert len(origen.dut.physical_pins) == 0 - -def test_add_and_retrieving_pins(): - assert len(origen.dut.pins) == 0 - p1 = origen.dut.add_pin("p1") - is_pin_group(p1) - assert len(origen.dut.pins) == 1 - -def test_retrieving_pins_using_pin_method(): - p1 = origen.dut.pin("p1") - is_pin_group(p1) - assert p1.name == "p1" - -def test_exception_on_duplicate_name(): - with pytest.raises(OSError): - origen.dut.add_pin("p1") - -def test_pin_group_default_state(): - p1 = origen.dut.pin("p1") - assert p1.name == "p1" - assert len(p1) == 1 - assert p1.data == 0 - assert p1.pin_actions == "Z" - assert p1.pin_names == ["p1"] - assert p1.physical_names == ["p1"] - assert p1.little_endian - assert not p1.big_endian - -def test_retrieving_pins_using_indexing(): - p1 = origen.dut.pins["p1"] - is_pin_group(p1) - assert p1.name == "p1" - -def test_retrieving_missing_pins(): - assert origen.dut.pin("blah") is None - with pytest.raises(KeyError): - origen.dut.pins['blah'] - -def test_adding_more_pins(): - p2 = origen.dut.add_pin("p2") - p3 = origen.dut.add_pin("p3") - is_pin_group(p2) - is_pin_group(p3) - assert len(origen.dut.pins) == 3 - assert "p2" in origen.dut.pins - assert "p3" in origen.dut.pins - _p2 = origen.dut.pin("p2") - _p3 = origen.dut.pin("p3") - is_pin_group(_p2) - is_pin_group(_p3) - assert _p2.name == "p2" - assert _p3.name == "p3" - -def test_grouping_pins(): grp = origen.dut.group_pins("grp", "p1", "p2", "p3") - is_pin_group(grp) - assert grp.name == "grp" - assert grp.pin_names == ["p1", "p2", "p3"] - assert len(origen.dut.pins) == 4 - assert len(grp) == 3 - assert len(origen.dut.physical_pins) == 3 assert grp.data == 0 assert grp.pin_actions == "ZZZ" -def test_exception_on_missing_pins(): - assert len(origen.dut.pins) == 4 - with pytest.raises(OSError): - origen.dut.group_pins("fail", "p2", "p3", "blah") - assert len(origen.dut.pins) == 4 - -def test_retrieving_groups(): - grp = origen.dut.pin("grp") - is_pin_group(grp) - _grp = origen.dut.pins["grp"] - is_pin_group(_grp) - assert grp.name == "grp" - assert _grp.name == "grp" - -def test_checking_names_within_group(): - grp = origen.dut.pins["grp"] - is_pin_group(grp) - assert "p1" in grp - assert "p2" in grp - assert "p3" in grp - -def test_setting_pin_data(): - # Observe that the underlying pin state has changed, therefore changing ALL references to that/those pin(s) - grp = origen.dut.pin("grp") - assert grp.data == 0 - grp.data = 0x3 - assert grp.data == 3 - assert origen.dut.pins["p1"].data == 1 - assert origen.dut.pins["p2"].data == 1 - assert origen.dut.pins["p3"].data == 0 - - origen.dut.pins["p3"].data = 1 - assert origen.dut.pins["p3"].data == 1 - assert grp.data == 7 - -def test_exception_on_overflow(): - grp = origen.dut.pin("grp") - assert grp.data == 7 - with pytest.raises(OSError): - grp.data = 8 - with pytest.raises(OSError): - grp.drive(8) - with pytest.raises(OSError): - grp.verify(8) - with pytest.raises(OSError): - grp.set(8) - assert grp.data == 7 - -# Basically make sure we don't get a Rust panic when garbage input is given. -# Would prefer to pass the error up through Python and cleanly fail. -def test_exception_on_bad_input(): - grp = origen.dut.pin("grp") - with pytest.raises(TypeError): - grp.data = "hi" - with pytest.raises(TypeError): - grp.drive({}) - with pytest.raises(TypeError): - grp.verify([]) - with pytest.raises(TypeError): - grp.set(origen) - -def test_driving_pins(): - # Same as above: changing the pin action sets it in the physical pin. - # Note that you cannot set pin states directly using strings. - grp = origen.dut.pin("grp") - assert grp.pin_actions == "ZZZ" - assert grp.drive() is None - assert grp.pin_actions == "DDD" - assert origen.dut.pins["p1"].pin_actions == "D" - assert origen.dut.pins["p2"].pin_actions == "D" - assert origen.dut.pins["p3"].pin_actions == "D" - -def test_capturing_and_highzing_pins(): - grp = origen.dut.pin("grp") - assert grp.pin_actions == "DDD" - assert grp.highz() is None - assert grp.pin_actions == "ZZZ" - assert origen.dut.pins["p1"].pin_actions == "Z" - assert origen.dut.pins["p2"].pin_actions == "Z" - assert origen.dut.pins["p3"].pin_actions == "Z" - - assert grp.capture() is None - assert grp.pin_actions == "CCC" - assert origen.dut.pins["p1"].pin_actions == "C" - assert origen.dut.pins["p2"].pin_actions == "C" - assert origen.dut.pins["p3"].pin_actions == "C" - -def test_driving_pins_with_data(): - grp = origen.dut.pin("grp") - assert grp.data == 7 - assert grp.pin_actions == "CCC" - assert grp.drive(5) is None - assert grp.data == 5 - assert grp.pin_actions == "DDD" - -def test_verifying_pins(): - grp = origen.dut.pin("grp") - assert grp.data == 5 - assert grp.pin_actions == "DDD" - assert grp.verify() is None - assert grp.pin_actions == "VVV" - - assert grp.verify(0) is None - assert grp.data == 0 - assert grp.pin_actions == "VVV" +def test_empty_pins(clean_falcon): + assert len(origen.dut.pins) == 0 + assert len(origen.dut.physical_pins) == 0 +def test_pin_group_base_state(clean_falcon): + p = origen.dut.add_pin("p0") + assert isinstance(p, _origen.dut.pins.PinGroup) + assert p.name == "p0" + assert p.reset_data == 0 + assert p.reset_actions == "Z" + assert p.data == 0 + assert p.pin_actions == "Z" + assert p.little_endian + assert not p.big_endian + assert p.width == 1 + assert p.physical_names == ["p0"] + assert p.pin_names == ["p0"] + assert p.width == 1 + +def test_pin_base_state(clean_falcon): + origen.dut.add_pin("p0") + p = origen.dut.physical_pin("p0") + assert isinstance(p, _origen.dut.pins.Pin) + assert p.name == "p0" + assert p.data == 0 + assert p.action == "HighZ" + assert p.reset_data is None + assert p.reset_action is None + assert p.aliases == [] + assert p.groups == {"p0": 0} + +class TestDefiningPins: + def test_adding_pins_with_width(self, clean_falcon): + origen.dut.add_pin("porta", width=4) + assert origen.dut.pins.keys() == ["porta0", "porta1", "porta2", "porta3", "porta"] + assert len(origen.dut.pins) == 5 + assert origen.dut.physical_pins.keys() == ["porta0", "porta1", "porta2", "porta3"] + assert len(origen.dut.physical_pins) == 4 + assert origen.dut.physical_pin("porta0").groups == {"porta": 0} + assert origen.dut.physical_pin("porta3").groups == {"porta": 3} + assert origen.dut.pin("porta").pin_names == ["porta0", "porta1", "porta2", "porta3"] + assert origen.dut.pin("porta").width == 4 + + def test_adding_pins_with_width_and_offset(self, clean_falcon): + origen.dut.add_pin("porta", width=2, offset=2) + assert origen.dut.pins.keys() == ["porta2", "porta3", "porta"] + assert len(origen.dut.pins) == 3 + + def test_adding_pins_with_width_1(self, clean_falcon): + origen.dut.add_pin("porta", width=1) + assert origen.dut.pins.keys() == ["porta0", "porta"] + assert len(origen.dut.pins) == 2 + + def test_adding_pins_with_width_1_offset_0(self, clean_falcon): + origen.dut.add_pin("porta", width=1, offset=0) + assert origen.dut.pins.keys() == ["porta0", "porta"] + assert len(origen.dut.pins) == 2 + + def test_adding_pins_with_reset_values(self, clean_falcon): + grp = origen.dut.add_pin("porta", width=4, reset_data=0xC, reset_action="DVDV") + assert grp.data == 0xC + assert grp.pin_actions == "VDVD" + assert grp.reset_data == 0xC + assert grp.reset_actions == "VDVD" + # Check the physical pins + assert origen.dut.physical_pin("porta0").reset_action == "V" + assert origen.dut.physical_pin("porta0").reset_data == 0 + assert origen.dut.physical_pin("porta1").reset_action == "D" + assert origen.dut.physical_pin("porta1").reset_data == 0 + assert origen.dut.physical_pin("porta2").reset_action == "V" + assert origen.dut.physical_pin("porta2").reset_data == 1 + assert origen.dut.physical_pin("porta3").reset_action == "D" + assert origen.dut.physical_pin("porta3").reset_data == 1 + + def test_exception_on_invalid_width(self, clean_falcon): + assert "porta" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.add_pin("porta", width=0) + with pytest.raises(OverflowError): + origen.dut.add_pin("porta", width=-1) + + def test_exception_on_invalid_offset(self, clean_falcon): + assert "porta" not in origen.dut.pins + with pytest.raises(OverflowError): + origen.dut.add_pin("porta", offset=-1) + + # Exception raised when the pin name has already been added. + def test_exception_on_explicit_duplicate(self, clean_falcon): + origen.dut.add_pin("p0") + assert len(origen.dut.pins) == 1 + assert len(origen.dut.physical_pins) == 1 + with pytest.raises(OSError): + origen.dut.add_pin("p0") + assert len(origen.dut.pins) == 1 + assert len(origen.dut.physical_pins) == 1 + + # Exception raised when a would-be pin name conflicts with an existing pin. + def test_exception_on_implicit_duplicate(self, clean_falcon): + origen.dut.add_pin("port", width=4) + assert len(origen.dut.pins) == 5 + with pytest.raises(OSError): + origen.dut.add_pin("port0") + assert len(origen.dut.pins) == 5 + + def test_exception_on_missing_pins(self, clean_falcon): + assert origen.dut.pin("blah") is None + with pytest.raises(KeyError): + origen.dut.pins['blah'] + + def test_exception_on_offset_with_no_width(self, clean_falcon): + assert len(origen.dut.pins) == 0 + with pytest.raises(OSError): + origen.dut.add_pin("invalid", offset=2) + assert len(origen.dut.pins) == 0 + + def test_exception_on_invalid_reset_data(self, clean_falcon): + assert "invalid" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.add_pin("invalid", reset_data=2) + with pytest.raises(OSError): + origen.dut.add_pin("invalid", width=2, reset_data=4) + with pytest.raises(OverflowError): + origen.dut.add_pin("invalid", width=2, reset_data=-1) + with pytest.raises(TypeError): + origen.dut.add_pin("invalid", width=2, reset_data="hi!") + assert "invalid" not in origen.dut.pins + + def test_exception_on_invalid_reset_actions(self, clean_falcon): + assert "invalid" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.add_pin("invalid", reset_action="ZZ") + with pytest.raises(OSError): + origen.dut.add_pin("invalid", width=2, reset_action="Z") + with pytest.raises(OSError): + origen.dut.add_pin("invalid", width=2, reset_action="ZZZ") + with pytest.raises(OSError): + origen.dut.add_pin("invalid", width=2, reset_action="HI") + with pytest.raises(OSError): + origen.dut.add_pin("invalid", width=2, reset_action="**") + with pytest.raises(TypeError): + origen.dut.add_pin("invalid", width=2, reset_action=42) + assert "invalid" not in origen.dut.pins + +class TestSettingStates: + def test_setting_data(self, clean_falcon, pins, grp): + # Observe that the underlying pin state has changed, + # therefore changing ALL references to that/those pin(s) + grp = origen.dut.pin("grp") + assert grp.data == 0 + grp.data = 0x3 + assert grp.data == 3 + assert origen.dut.pins["p1"].data == 1 + assert origen.dut.pins["p2"].data == 1 + assert origen.dut.pins["p3"].data == 0 + + # Set the data in a pin reference in the above group and ensure + # that the update is reflected there. + origen.dut.pins["p3"].data = 1 + assert origen.dut.pins["p3"].data == 1 + assert grp.data == 7 + + def test_driving_pins(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + assert grp.drive() is None + assert grp.pin_actions == "DDD" + assert grp.data == 0 + assert origen.dut.pins["p1"].pin_actions == "D" + assert origen.dut.pins["p2"].pin_actions == "D" + assert origen.dut.pins["p3"].pin_actions == "D" + + def test_driving_pins_with_data(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + assert grp.drive(0x7) is None + assert grp.data == 0x7 + assert grp.pin_actions == "DDD" + + def test_veriying_pins(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + assert grp.verify() is None + assert grp.data == 0 + assert grp.pin_actions == "VVV" + + def test_veriying_pins_with_data(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + assert grp.verify(0x7) is None + assert grp.data == 0x7 + assert grp.pin_actions == "VVV" + + def test_capturing_pins(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + assert grp.capture() is None + assert grp.pin_actions == "CCC" + assert origen.dut.pins["p1"].pin_actions == "C" + assert origen.dut.pins["p2"].pin_actions == "C" + assert origen.dut.pins["p3"].pin_actions == "C" + + def test_tristating_pins(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + grp.drive() + assert grp.pin_actions == "DDD" + assert grp.highz() is None + assert grp.pin_actions == "ZZZ" + assert origen.dut.pins["p1"].pin_actions == "Z" + assert origen.dut.pins["p2"].pin_actions == "Z" + assert origen.dut.pins["p3"].pin_actions == "Z" + + def test_resetting_data(self, clean_falcon): + grp = origen.dut.add_pin("porta", width=4, reset_data=0xC, reset_action="DVDV") + assert grp.data == 0xC + assert grp.pin_actions == "VDVD" + grp.drive(0x3) + assert grp.data == 0x3 + assert grp.pin_actions == "DDDD" + grp.reset() + assert grp.data == 0xC + assert grp.pin_actions == "VDVD" + + def test_exception_on_overflow_data(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + grp.data = 7 + assert grp.data == 7 + with pytest.raises(OSError): + grp.data = 8 + with pytest.raises(OSError): + grp.set(8) + assert grp.data == 7 + + def test_exception_on_invalid_data(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + with pytest.raises(TypeError): + grp.data = "hi" + with pytest.raises(TypeError): + grp.set({}) + with pytest.raises(TypeError): + grp.data = [] + with pytest.raises(TypeError): + grp.set(origen) + with pytest.raises(OverflowError): + grp.data = -1 + + def test_exception_on_overflow_actions(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + grp.data = 7 + assert grp.data == 7 + assert grp.pin_actions == "ZZZ" + with pytest.raises(OSError): + grp.drive(8) + with pytest.raises(OSError): + grp.verify(8) + assert grp.pin_actions == "ZZZ" + assert grp.data == 7 + + def test_exception_on_invalid_actions(self, clean_falcon, pins, grp): + grp = origen.dut.pin("grp") + with pytest.raises(TypeError): + grp.drive({}) + with pytest.raises(TypeError): + grp.verify([]) + +class TestPinAliasing: + + def test_aliasing_pins(self, clean_falcon, pins, grp, ports): + assert "a1" not in origen.dut.pins + origen.dut.add_pin_alias("p1", "a1") + assert "a1" in origen.dut.pins + a1 = origen.dut.pin("a1") + is_pin_group(a1) + assert len(a1) == 1 + assert a1.pin_names == ["p1"] + + def test_adding_multiple_aliases(self, clean_falcon, pins): + origen.dut.add_pin_alias("p1", "a1_a", "a1_b", "a1_c") + check_alias("p1", "a1_a") + check_alias("p1", "a1_b") + check_alias("p1", "a1_c") + + def test_aliasing_pins_and_groups(self, clean_falcon, ports): + origen.dut.add_pin_alias("porta", "pta", "pA") + assert origen.dut.pin("pta").pin_names == origen.dut.pin("porta").pin_names + assert origen.dut.pin("pA").pin_names == origen.dut.pin("porta").pin_names + + def test_actions_and_data_using_aliases(self, clean_falcon, pins): + origen.dut.add_pin_alias("p1", "a1") + p1 = origen.dut.pin("p1") + a1 = origen.dut.pin("a1") + assert a1.drive(1) is None + assert a1.data == 1 + assert a1.pin_actions == "D" + assert p1.data == 1 + assert p1.pin_actions == "D" + + assert a1.verify(0) is None + assert a1.data == 0 + assert a1.pin_actions == "V" + assert p1.data == 0 + assert p1.pin_actions == "V" + + def test_aliasing_an_alias(self, clean_falcon, pins, grp, ports): + origen.dut.add_pin_alias("p1", "a1") + origen.dut.add_pin_alias("a1", "_a1") + assert "_a1" in origen.dut.pins + assert origen.dut.pins["_a1"].pin_names == ["p1"] -def test_pins_in_subblocks(): + def test_exception_on_duplicate_aliases(self, clean_falcon, pins, grp, ports): + assert "a1" not in origen.dut.pins + origen.dut.add_pin_alias("p1", "a1") + assert "a1" in origen.dut.pins + with pytest.raises(OSError): + origen.dut.add_pin_alias("p1", "a1") + + def test_exception_on_aliasing_missing_pin(self, clean_falcon, pins, grp, ports): + assert "blah" not in origen.dut.pins + assert "alias_blah" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.add_pin_alias("blah", "alias_blah") + assert "blah" not in origen.dut.pins + assert "alias_blah" not in origen.dut.pins + +class TestGrouping: + def test_grouping_pins_by_name(self, clean_falcon, pins): + grp = origen.dut.group_pins("grp", "p0", "p1", "p2") + is_pin_group(grp) + assert grp.name == "grp" + assert grp.pin_names == ["p0", "p1", "p2"] + assert grp.little_endian + assert "p0" in grp + assert "p1" in grp + assert "p2" in grp + + def test_grouping_pins_by_regex(self, clean_falcon, pins, ports): + import re + r = re.compile("port.0") + grp = origen.dut.group_pins("grp", r) + assert grp.pin_names == ["porta0", "portb0"] + + def test_collecting_using_ruby_like_syntax(self, clean_falcon, pins, ports): + grp = origen.dut.group_pins("grp", "/port.0/") + assert grp.pin_names == ["porta0", "portb0"] + + def test_grouping_pins_by_mixture(self, clean_falcon, pins, ports): + import re + r = re.compile("port.0") + grp = origen.dut.group_pins("grp", "/port.1/", "p1", r) + assert grp.pin_names == ["porta1", "portb1", "p1", "porta0", "portb0"] + + def test_big_endian(self, clean_falcon): + pins = origen.dut.add_pin("portc", width=4, little_endian=False) + assert pins.pin_names == ["portc3", "portc2", "portc1", "portc0"] + assert not pins.little_endian + assert pins.big_endian + + def test_little_endian(self, clean_falcon): + pins = origen.dut.add_pin("portd", width=4, little_endian=True) + assert pins.pin_names == ["portd0", "portd1", "portd2", "portd3"] + assert pins.little_endian + assert not pins.big_endian + + def test_big_endian_with_offset(self, clean_falcon): + pins = origen.dut.add_pin("porte", width=2, little_endian=False, offset=2) + assert pins.pin_names == ["porte3", "porte2"] + assert not pins.little_endian + assert pins.big_endian + + def test_grouping_mixed_endianness(self, clean_falcon): + origen.dut.add_pin("portc", width=4, little_endian=False) + origen.dut.add_pin("portd", width=4, little_endian=True) + grp = origen.dut.group_pins("mixed_endianness", "portc", "portd") + assert grp.pin_names == ["portc3", "portc2", "portc1", "portc0", "portd0", "portd1", "portd2", "portd3"] + assert grp.little_endian == True + assert grp.big_endian == False + + def test_grouping_big_endian(self, clean_falcon): + origen.dut.add_pin("portc", width=4, little_endian=False) + origen.dut.add_pin("portd", width=4, little_endian=True) + grp = origen.dut.group_pins("big_endian", "portc", "portd", little_endian=False) + assert grp.pin_names == ["portd3", "portd2", "portd1", "portd0", "portc0", "portc1", "portc2", "portc3"] + assert grp.little_endian == False + assert grp.big_endian == True + + def test_exception_on_missing_pins(self, clean_falcon, pins): + assert "fail" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.group_pins("fail", "p0", "p1", "blah") + assert "fail" not in origen.dut.pins + + def test_exception_on_grouping_duplicates(self, clean_falcon, pins, grp): + with pytest.raises(OSError): + origen.dut.group_pins("invalid", "p1", "p1", "p1") + assert "invalid" not in origen.dut.pins + + def test_exception_on_grouping_aliases_of_the_same_pin(self, clean_falcon, pins, grp): + origen.dut.add_pin_alias("p1", "a1") + with pytest.raises(OSError): + origen.dut.group_pins("invalid", "p1", "p2", "a1", "p3") + assert "invalid" not in origen.dut.pins + + def test_exception_on_grouping_duplicates_when_nested(self, clean_falcon, ports): + assert "porta" in origen.dut.pins + assert "porta0" in origen.dut.pins + assert "porta1" in origen.dut.pins + assert "invalid" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.group_pins("grouping_porta", "porta", "porta0", "porta1") + assert "invalid" not in origen.dut.pins + +class TestCollecting: + def test_collecting_pins(self, clean_falcon, pins): + n = len(origen.dut.pins) + # Create an anonymous pin group (pin collection) + c = origen.dut.pins.collect("p1", "p2") + assert len(origen.dut.pins) == n + is_pin_collection(c) + assert c.pin_names == ["p1", "p2"] + + def test_pin_collection_initial_state(self, clean_falcon, pins): + origen.dut.pin("p1").drive(1) + origen.dut.pin("p2").drive(0) + origen.dut.pin("p3").drive(1) + c = origen.dut.pins.collect("p1", "p2", "p3") + assert c.data == 0x5 + assert c.pin_actions == "DDD" + + def test_collecting_with_single_regex(self, clean_falcon, pins, ports): + import re + r = re.compile("port.0") + c = origen.dut.pins.collect(r) + assert c.pin_names == ["porta0", "portb0"] + + def test_collecting_using_ruby_like_syntax(self, clean_falcon, pins, ports): + c = origen.dut.pins.collect("/port.0/") + assert c.pin_names == ["porta0", "portb0"] + + def test_collecting_with_mixed_inputs(self, clean_falcon, pins, ports): + import re + r = re.compile("port.0") + c = origen.dut.pins.collect("/port.1/", "p1", r) + assert c.pin_names == ["porta1", "portb1", "p1", "porta0", "portb0"] + + def test_pin_collection_getting_and_setting_data(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + assert c.data == 0x0 + c.data = 0x7 + assert c.data == 0x7 + assert origen.dut.physical_pin("p1").data == 1 + assert origen.dut.physical_pin("p2").data == 1 + assert origen.dut.physical_pin("p3").data == 1 + c.set(0x1) + assert c.data == 0x1 + assert origen.dut.physical_pin("p1").data == 1 + assert origen.dut.physical_pin("p2").data == 0 + assert origen.dut.physical_pin("p3").data == 0 + + def test_driving_pin_collection(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + c.drive() + assert c.pin_actions == "DDD" + assert origen.dut.physical_pin("p1").action == "Drive" + assert origen.dut.physical_pin("p2").action == "Drive" + assert origen.dut.physical_pin("p3").action == "Drive" + + def test_driving_pin_collection_with_data(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + c.drive(0x7) + assert c.pin_actions == "DDD" + assert c.data == 0x7 + + def test_verifying_pin_collection(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + c.verify() + assert c.pin_actions == "VVV" + assert origen.dut.physical_pin("p1").action == "Verify" + assert origen.dut.physical_pin("p2").action == "Verify" + assert origen.dut.physical_pin("p3").action == "Verify" + + def test_verifying_pin_collection_with_data(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + c.verify(0x5) + assert c.pin_actions == "VVV" + assert c.data == 0x5 + + def test_tristating_pin_collection(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + c.drive() + assert c.pin_actions == "DDD" + c.highz() + assert c.pin_actions == "ZZZ" + assert origen.dut.physical_pin("p1").action == "HighZ" + assert origen.dut.physical_pin("p2").action == "HighZ" + assert origen.dut.physical_pin("p3").action == "HighZ" + + def test_capturing_pin_collection(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + c.capture() + assert c.pin_actions == "CCC" + assert origen.dut.physical_pin("p1").action == "Capture" + assert origen.dut.physical_pin("p2").action == "Capture" + assert origen.dut.physical_pin("p3").action == "Capture" + + def test_reset_values_persist_in_collections(self, clean_falcon, pins): + origen.dut.add_pin("port", width=2, reset_data=0x3, reset_action="DD") + c = origen.dut.pins.collect("p0", "p1", "port") + assert c.reset_data == 0xC + assert c.reset_actions == "ZZDD" + assert c.data == 0xC + assert c.pin_actions == "ZZDD" + + def test_resetting_collection(self, clean_falcon, pins): + origen.dut.add_pin("port", width=2, reset_data=0x3, reset_action="DD") + c = origen.dut.pins.collect("p0", "p1", "port") + c.verify(0x3) + assert c.data == 0x3 + assert c.pin_actions == "VVVV" + c.reset() + assert c.data == 0xC + assert c.pin_actions == "ZZDD" + + def test_chaining_method_calls_with_nonsticky_mask(self, clean_falcon, pins): + # This should set the data to 0x3, then drive the pins using mask 0x2. + # The mask should then be cleared. + c = origen.dut.pins.collect("p0", "p1") + c.data = 0x0 + c.highz() + assert c.data == 0 + assert c.pin_actions == "ZZ" + + c.set(0x3).with_mask(0x2).drive() + assert c.data == 0x3 + assert c.pin_actions == "DZ" + + # This should set the data and action regardless of the mask being used previously. + c.data = 0x0 + c.highz() + assert c.data == 0 + assert c.pin_actions == "ZZ" + + # This should set the data and pin action using the mask 0x1. + # The mask should then be cleared. + c.with_mask(0x1).set(0x3).verify() + assert c.data == 0x1 + assert c.pin_actions == "ZV" + + # This should set the data and action regardless of the mask being used previously. + c.data = 0x0 + c.highz() + assert c.data == 0 + assert c.pin_actions == "ZZ" + + def test_collecting_mixed_endianness(self, clean_falcon): + origen.dut.add_pin("portc", width=4, little_endian=False) + origen.dut.add_pin("portd", width=4, little_endian=True) + c = origen.dut.pins.collect("portc", "portd") + assert c.pin_names == ["portc3", "portc2", "portc1", "portc0", "portd0", "portd1", "portd2", "portd3"] + assert c.little_endian == True + assert c.big_endian == False + + def test_collecting_big_endian(self, clean_falcon): + origen.dut.add_pin("portc", width=4, little_endian=False) + origen.dut.add_pin("portd", width=4, little_endian=True) + c = origen.dut.pins.collect("portc", "portd", little_endian=False) + assert c.pin_names == ["portd3", "portd2", "portd1", "portd0", "portc0", "portc1", "portc2", "portc3"] + assert c.little_endian == False + assert c.big_endian == True + + def test_getting_nested_pin_data(self, clean_falcon, ports): + grp = origen.dut.group_pins("ports", "porta", "portb") + c = origen.dut.pins.collect("porta", "portb") + assert grp.data == 0 + assert c.data == 0 + + def test_getting_nested_pin_actions(self, clean_falcon, ports): + grp = origen.dut.group_pins("ports", "porta", "portb") + c = origen.dut.pins.collect("porta", "portb") + assert grp.pin_actions == "ZZZZZZ" + assert c.pin_actions == "ZZZZZZ" + + def test_setting_nested_pin_data(self, clean_falcon, ports): + grp = origen.dut.group_pins("ports", "porta", "portb") + c = origen.dut.pins.collect("porta", "portb") + grp.data = 0x2A + assert grp.data == 0x2A + assert c.data == 0x2A + c.data = 0x15 + assert c.data == 0x15 + assert grp.data == 0x15 + + def test_setting_nested_pin_actions(self, clean_falcon, ports): + grp = origen.dut.group_pins("ports", "porta", "portb") + c = origen.dut.pins.collect("porta", "portb") + grp.capture() + assert grp.pin_actions == "CCCCCC" + assert c.pin_actions == "CCCCCC" + c.verify() + assert c.pin_actions == "VVVVVV" + assert grp.pin_actions == "VVVVVV" + + def test_exception_on_collecting_missing_pins(self, clean_falcon, pins): + with pytest.raises(OSError): + origen.dut.pins.collect("p1", "p2", "blah") + + def test_exception_on_collecting_duplicate_pins(self, clean_falcon, pins): + origen.dut.add_pin_alias("p1", "a1") + origen.dut.add_pin_alias("a1", "a1_a", "a1_b", "a1_c") + with pytest.raises(OSError): + origen.dut.pins.collect("p1", "p1", "p1") + with pytest.raises(OSError): + origen.dut.pins.collect("p1", "a1") + with pytest.raises(OSError): + origen.dut.pins.collect("a1_a", "a1_b", "a1_c") + + def test_exception_on_overflow_data(self, clean_falcon, pins): + c = origen.dut.pins.collect("p1", "p2", "p3") + with pytest.raises(OSError): + c.data = 0xF + with pytest.raises(OverflowError): + c.data = -1 + assert c.data == 0x0 + + def test_exception_on_collecting_duplicates_when_nested(self, clean_falcon, ports): + assert "porta" in origen.dut.pins + assert "porta0" in origen.dut.pins + assert "porta1" in origen.dut.pins + assert "invalid" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.pins.collect("invalid", "porta", "porta0", "porta1") + assert "invalid" not in origen.dut.pins + + def test_exception_on_collecting_duplicates_when_nested_2(self, clean_falcon, ports): + origen.dut.group_pins("index_0s", "porta0", "portb0") + origen.dut.group_pins("index_0s_rev", "portb0", "porta0") + assert "invalid" not in origen.dut.pins + with pytest.raises(OSError): + origen.dut.pins.collect("invalid", "index_0s", "index_0s_rev") + assert "invalid" not in origen.dut.pins + +def test_pins_in_subblocks(clean_falcon, pins): # We should have pins at the DUT level, but not in any subblocks. assert len(origen.dut.pins) == 4 assert len(origen.dut.sub_blocks["core1"].pins) == 0 @@ -228,190 +732,17 @@ def test_pins_in_subblocks(): assert len(origen.dut.pins) == 4 assert origen.dut.pin("_p1") is None -def test_adding_aliases(): - origen.dut.add_pin_alias("p1", "a1") - assert len(origen.dut.pins) == 5 - assert len(origen.dut.physical_pins) == 3 - assert "a1" in origen.dut.pins - a1 = origen.dut.pin("a1") - is_pin_group(a1) - assert len(a1) == 1 - assert a1.pin_names == ["p1"] - -def test_driving_an_alias(): - a1 = origen.dut.pin("a1") - p1 = origen.dut.pin("p1") - assert a1.drive(1) is None - assert a1.data == 1 - assert a1.pin_actions == "D" - assert p1.data == 1 - assert p1.pin_actions == "D" - - assert a1.verify(0) is None - assert a1.data == 0 - assert a1.pin_actions == "V" - assert p1.data == 0 - assert p1.pin_actions == "V" - -def test_adding_multiple_aliases(): - origen.dut.add_pin_alias("p1", "a1_a", "a1_b", "a1_c") - check_alias("p1", "a1_a") - check_alias("p1", "a1_b") - check_alias("p1", "a1_c") - -def test_exception_on_duplicate_aliases(): - with pytest.raises(OSError): - origen.dut.add_pin_alias("p1", "a1") - -def test_exception_when_aliasing_missing_pin(): - with pytest.raises(OSError): - origen.dut.add_pin_alias("blah", "alias_blah") - -def test_aliasing_an_alias(): - origen.dut.add_pin_alias("a1", "_a1") - assert "_a1" in origen.dut.pins - assert origen.dut.pins["_a1"].pin_names == ["p1"] - -def test_exception_on_grouping_the_same_pin(): - with pytest.raises(OSError): - origen.dut.group_pins("test_grouping_the_same_pin", "p1", "p1", "p1") - -def test_exception_on_grouping_aliases_of_the_same_pin(): - with pytest.raises(OSError): - origen.dut.group_pins("test_grouping_aliases_of_same_pin", "p2", "p3", "a1_a", "a1_b", "a1_c") - -def test_collecting_pins(): - n = len(origen.dut.pins) - # Create an anonymous pin group (pin collection) - c = origen.dut.pins.collect("p1", "p2") - assert len(origen.dut.pins) == n - is_pin_collection(c) - -def test_exception_on_collecting_missing_pins(): - with pytest.raises(OSError): - origen.dut.pins.collect("p1", "p2", "blah") - -def test_exception_on_collecting_duplicate_pins(): - with pytest.raises(OSError): - origen.dut.pins.collect("p1", "p1", "p1") - with pytest.raises(OSError): - origen.dut.pins.collect("p1", "a1") - with pytest.raises(OSError): - origen.dut.pins.collect("a1_a", "a1_b", "a1_c") - -def test_pin_collection_initial_state(): - origen.dut.pin("p1").drive(1) - origen.dut.pin("p2").drive(0) - origen.dut.pin("p3").drive(1) - - c = origen.dut.pins.collect("p1", "p2", "p3") - is_pin_collection(c) - assert c.data == 0x5 - assert c.pin_actions == "DDD" - -def test_pin_collection_getting_and_setting_data(): - c = origen.dut.pins.collect("p1", "p2", "p3") - c.data = 0x7 - assert c.data == 0x7 - assert origen.dut.physical_pin("p1").data == 1 - assert origen.dut.physical_pin("p2").data == 1 - assert origen.dut.physical_pin("p3").data == 1 - - c.set(0x1) - assert c.data == 0x1 - assert origen.dut.physical_pin("p1").data == 1 - assert origen.dut.physical_pin("p2").data == 0 - assert origen.dut.physical_pin("p3").data == 0 - - with pytest.raises(OSError): - c.data = 8 - assert c.data == 0x1 - -def test_pin_collectino_getting_and_setting_actions(): - c = origen.dut.pins.collect("p1", "p2", "p3") - c.verify() - assert c.pin_actions == "VVV" - assert origen.dut.physical_pin("p1").action == "Verify" - assert origen.dut.physical_pin("p2").action == "Verify" - assert origen.dut.physical_pin("p3").action == "Verify" - - c.highz() - assert c.pin_actions == "ZZZ" - assert origen.dut.physical_pin("p1").action == "HighZ" - assert origen.dut.physical_pin("p2").action == "HighZ" - assert origen.dut.physical_pin("p3").action == "HighZ" - -def test_pins_dict_like_api(): - # Check '__contains__' - assert "p1" in origen.dut.pins - - # Check '__getitem__' (indexing) - assert origen.dut.pins["p1"].name == "p1" - - # Check 'keys()' - keys = origen.dut.pins.keys() - assert isinstance(keys, list) - assert {'a1_c', 'a1', 'a1_b', 'grp', 'p1', 'p2', '_a1', 'p3', 'a1_a'} == set(keys) - - # Check 'values()' - values = origen.dut.pins.values() - assert len(values) == 9 - assert isinstance(values[0], _origen.dut.pins.PinGroup) - for name in origen.dut.pins: - assert name in keys - - # Check 'items()' - for n, p in origen.dut.pins.items(): - assert isinstance(n, str) - is_pin_group(p) - - # Check __len__ - assert len(origen.dut.pins) == 9 - - # Check 'to_dict' conversion - d = dict(origen.dut.pins) - assert isinstance(d, dict) - assert isinstance(d["p1"], _origen.dut.pins.PinGroup) - assert isinstance(d["p2"], _origen.dut.pins.PinGroup) - assert len(d) == 9 - -def test_physical_pins_dict_like_api(): - # check __contains__ - assert "p1" in origen.dut.physical_pins - - # check __len__ - assert len(origen.dut.physical_pins) == 3 - - # Check '__getitem__' (indexing) - pin = origen.dut.physical_pins["p1"] - is_pin(pin) - - pin_names = ["p1", "p2", "p3"] - # Check keys() - assert set(origen.dut.physical_pins.keys()) == set(pin_names) - - # Check values() - for v in origen.dut.physical_pins.values(): - is_pin(v) - assert v.name in pin_names - - # Check items() - for n, p in origen.dut.physical_pins.items(): - assert isinstance(n, str) - is_pin(p) - - # Check iterating - for name in origen.dut.physical_pins: - assert name in pin_names - - # Check 'to_dict' conversion - d = dict(origen.dut.physical_pins) - assert isinstance(d, dict) - assert len(d) == 3 - assert isinstance(d["p1"], _origen.dut.pins.Pin) - assert isinstance(d["p2"], _origen.dut.pins.Pin) - -def test_pin_group_list_like_api(): +def test_pin_loader_api(clean_eagle): + assert origen.dut.pins.keys() == [ + "porta0", "porta1", "porta", + "portb0", "portb1", "portb2", "portb3", "portb", + "portc0", "portc1", "portc", + "clk", "swd_clk", "tclk" + ] + assert origen.dut.pin("portc").reset_data == 0x3 + assert origen.dut.pin("clk").reset_actions == "D" + +def test_pin_group_list_like_api(clean_falcon, pins, grp, ports): grp = origen.dut.pin("grp") # Check '__contains__' @@ -439,7 +770,6 @@ def test_pin_group_list_like_api(): assert item.pin_names == names[i] def test_pin_collection_from_pin_group(): - origen.dut.add_pin("p0") origen.dut.group_pins("p", "p0", "p1", "p2", "p3") grp = origen.dut.pins["p"] @@ -487,335 +817,6 @@ def test_exception_on_out_of_bounds_indexing(): with pytest.raises(OSError): c[0:100] -def test_chaining_method_calls_with_nonsticky_mask(): - # This should set the data to 0x3, then drive the pins using mask 0x2. - # The mask should then be cleared. - c = origen.dut.pins.collect("p0", "p1") - c.data = 0x0 - c.highz() - assert c.data == 0 - assert c.pin_actions == "ZZ" - - c.set(0x3).with_mask(0x2).drive() - assert c.data == 0x3 - assert c.pin_actions == "DZ" - - # This should set the data and action regardless of the mask being used previously. - c.data = 0x0 - c.highz() - assert c.data == 0 - assert c.pin_actions == "ZZ" - - # This should set the data and pin action using the mask 0x1. - # The mask should then be cleared. - c.with_mask(0x1).set(0x3).verify() - assert c.data == 0x1 - assert c.pin_actions == "ZV" - - # This should set the data and action regardless of the mask being used previously. - c.data = 0x0 - c.highz() - assert c.data == 0 - assert c.pin_actions == "ZZ" - -# def test_chaining_method_calls_with_sticky_mask(): -# ... - -### Get a clean DUT here ### - -def test_reseting_DUT(): - origen.app.instantiate_dut("dut.falcon") - # Ensure the DUT was set and the pin container is available. - assert origen.dut - assert isinstance(origen.dut.pins, _origen.dut.pins.PinContainer) - assert len(origen.dut.pins) == 0 - assert len(origen.dut.physical_pins) == 0 - -def test_adding_multiple_pins(): - # !!! - # This should add two physical pins and one pin group, containing both physical pins. - # !!! - porta = origen.dut.add_pin("porta", width=2) - is_pin_group(porta) - assert porta.name == "porta" - assert len(porta) == 2 - assert len(origen.dut.physical_pins) == 2 - assert len(origen.dut.pins) == 3 - assert set(origen.dut.physical_pins.pin_names) == {"porta0", "porta1"} - assert set(origen.dut.pins.pin_names) == {"porta0", "porta1", "porta"} - - # This should add four physical pins and one pin group, but the indexing should start a 1 instead of 0. - portb = origen.dut.add_pin("portb", width=4, offset=1) - is_pin_group(portb) - assert portb.name == "portb" - assert len(portb) == 4 - assert len(origen.dut.physical_pins) == 6 - assert len(origen.dut.pins) == 8 - assert set(origen.dut.physical_pins.pin_names) == {"porta0", "porta1", "portb1", "portb2", "portb3", "portb4"} - assert set(origen.dut.pins.pin_names) == {"porta0", "porta1", "porta", "portb1", "portb2", "portb3", "portb4", "portb"} - -def test_adding_single_pins_with_width_and_offset(): - # Corner case for adding single pins. Normally, you'd not use these options for a single one, but there's no reason why - # it should work any differently. - origen.dut.add_pin("portc", width=1, offset=1) - assert len(origen.dut.physical_pins) == 7 - assert len(origen.dut.pins) == 10 - assert "portc1" in origen.dut.physical_pins - assert "portc" in origen.dut.pins - assert "portc1" in origen.dut.pins - - origen.dut.add_pin("portd", width=1) - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - assert "portd0" in origen.dut.physical_pins - assert "portd" in origen.dut.pins - assert "portd0" in origen.dut.pins - -### Note: pyo3 will throw an overflow error on negative numbers. - -def test_exception_on_invalid_width(): - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - with pytest.raises(OSError): - origen.dut.add_pin("porte", width=0) - with pytest.raises(OverflowError): - origen.dut.add_pin("porte", width=-1) - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - -def test_exception_on_invalid_offset(): - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - with pytest.raises(OverflowError): - origen.dut.add_pin("porte", width=1, offset=-1) - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - -def test_exception_on_offset_without_width(): - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - with pytest.raises(OSError): - origen.dut.add_pin("porte", offset=1) - assert len(origen.dut.physical_pins) == 8 - assert len(origen.dut.pins) == 12 - -# !!! -# Reseting DUT -# !!! - -def test_length_vs_width(): - origen.app.instantiate_dut("dut.falcon") - assert origen.dut - origen.dut.add_pin("porta", width=2) - origen.dut.add_pin("portb", width=4) - assert len(origen.dut.physical_pins) == 6 - assert len(origen.dut.pins) == 8 - grp = origen.dut.group_pins("ports", "porta", "portb") - assert len(grp) == 6 - assert grp.width == 6 - - c = origen.dut.pins.collect("porta", "portb") - assert len(c) == 6 - assert c.width == 6 - -def test_getting_nested_pin_data(): - grp = origen.dut.pin("ports") - c = origen.dut.pins.collect("porta", "portb") - - assert grp.data == 0 - assert c.data == 0 - -def test_getting_nested_pin_actions(): - grp = origen.dut.pin("ports") - c = origen.dut.pins.collect("porta", "portb") - - assert grp.pin_actions == "ZZZZZZ" - assert c.pin_actions == "ZZZZZZ" - -def test_setting_nested_pin_data(): - grp = origen.dut.pin("ports") - c = origen.dut.pins.collect("porta", "portb") - - grp.data = 0x2A - assert grp.data == 0x2A - assert c.data == 0x2A - c.data = 0x15 - assert c.data == 0x15 - assert grp.data == 0x15 - -def test_setting_nested_pin_actions(): - grp = origen.dut.pin("ports") - c = origen.dut.pins.collect("porta", "portb") - - grp.capture() - assert grp.pin_actions == "CCCCCC" - assert c.pin_actions == "CCCCCC" - c.verify() - assert c.pin_actions == "VVVVVV" - assert grp.pin_actions == "VVVVVV" - -def test_exception_on_grouping_duplicates_when_nested(): - assert len(origen.dut.physical_pins) == 6 - assert len(origen.dut.pins) == 9 - assert "porta" in origen.dut.pins - assert "porta0" in origen.dut.pins - assert "porta1" in origen.dut.pins - with pytest.raises(OSError): - origen.dut.group_pins("grouping_porta", "porta", "porta0", "porta1") - assert len(origen.dut.physical_pins) == 6 - assert len(origen.dut.pins) == 9 - -def test_exception_on_collecting_duplicates_when_nested(): - assert "porta" in origen.dut.pins - assert "porta0" in origen.dut.pins - assert "porta1" in origen.dut.pins - with pytest.raises(OSError): - origen.dut.pins.collect("collecting_porta", "porta", "porta0", "porta1") - -def test_exception_on_collecting_duplicates_when_nested_2(): - origen.dut.group_pins("index_0s", "porta0", "portb0") - origen.dut.group_pins("index_0s_rev", "portb0", "porta0") - assert "error" not in origen.dut.pins - with pytest.raises(OSError): - origen.dut.pins.collect("error", "index_0s", "index_0s_rev") - assert "error" not in origen.dut.pins - -def test_big_endian(): - pins = origen.dut.add_pin("portc", width=4, little_endian=False) - assert pins.pin_names == ["portc3", "portc2", "portc1", "portc0"] - assert not pins.little_endian - assert pins.big_endian - -def test_little_endian(): - pins = origen.dut.add_pin("portd", width=4, little_endian=True) - assert pins.pin_names == ["portd0", "portd1", "portd2", "portd3"] - assert pins.little_endian - assert not pins.big_endian - -def test_big_endian_with_offset(): - pins = origen.dut.add_pin("porte", width=2, little_endian=False, offset=2) - assert pins.pin_names == ["porte3", "porte2"] - assert not pins.little_endian - assert pins.big_endian - -def test_grouping_mixed_endianness(): - grp = origen.dut.group_pins("mixed_endianness", "portc", "portd") - assert grp.pin_names == ["portc3", "portc2", "portc1", "portc0", "portd0", "portd1", "portd2", "portd3"] - assert grp.little_endian == True - assert grp.big_endian == False - -def test_grouping_big_endian(): - grp = origen.dut.group_pins("big_endian", "portc", "portd", little_endian=False) - assert grp.pin_names == ["portd3", "portd2", "portd1", "portd0", "portc0", "portc1", "portc2", "portc3"] - assert grp.little_endian == False - assert grp.big_endian == True - -def test_collecting_mixed_endianness(): - c = origen.dut.pins.collect("portc", "portd") - assert c.pin_names == ["portc3", "portc2", "portc1", "portc0", "portd0", "portd1", "portd2", "portd3"] - assert c.little_endian == True - assert c.big_endian == False - -def test_collecting_big_endian(): - c = origen.dut.pins.collect("portc", "portd", little_endian=False) - assert c.pin_names == ["portd3", "portd2", "portd1", "portd0", "portc0", "portc1", "portc2", "portc3"] - assert c.little_endian == False - assert c.big_endian == True - -# # !!! -# # RESET DUT -# # !!! - -def test_default_reset_values(): - origen.app.instantiate_dut("dut.falcon") - assert origen.dut - origen.dut.add_pin("p0") - assert origen.dut.physical_pin("p0").reset_data == None - assert origen.dut.physical_pin("p0").reset_action == None - assert origen.dut.pin("p0").reset_data == 0 - assert origen.dut.pin("p0").reset_actions == 'Z' - -def test_default_reset_values_with_width(): - origen.dut.add_pin("porta", width=2) - assert origen.dut.physical_pin("porta0").reset_data == None - assert origen.dut.physical_pin("porta0").reset_action == None - assert origen.dut.physical_pin("porta1").reset_data == None - assert origen.dut.physical_pin("porta1").reset_action == None - assert origen.dut.pin("porta").reset_data == 0 - assert origen.dut.pin("porta").reset_actions == "ZZ" - -def test_adding_with_reset_values(): - origen.dut.add_pin("p1", reset_data=1, reset_action="D") - assert origen.dut.physical_pin("p1").reset_data == 1 - assert origen.dut.physical_pin("p1").reset_action == "D" - assert origen.dut.pin("p1").reset_data == 1 - assert origen.dut.pin("p1").reset_actions == "D" - -def test_adding_with_reset_values_with_width(): - origen.dut.add_pin("portb", width=3, reset_data=0b101, reset_action="VDV") - assert origen.dut.pin("portb").reset_data == 0b101 - assert origen.dut.pin("portb").reset_actions == "VDV" - -def test_reset_values_persist_in_groups(): - grp = origen.dut.group_pins("grp1", "p1", "p0", "porta", "portb") - assert grp.data == 0b101_00_0_1 - assert grp.pin_actions == "DZZZVDV" - assert grp.reset_data == 0b101_00_0_1 - assert grp.reset_actions == "DZZZVDV" - -def test_reset_values_persist_in_collections(): - c = origen.dut.pins.collect("p1", "p0", "porta", "portb") - assert c.data == 0b101_00_0_1 - assert c.pin_actions == "DZZZVDV" - assert c.reset_data == 0b101_00_0_1 - assert c.reset_actions == "DZZZVDV" - -def test_resetting_pins(): - pins = origen.dut.pin("portb") - pins.set(0b111) - pins.drive() - assert pins.data == 0b111 - assert pins.pin_actions == "DDD" - pins.reset() - assert pins.data == 0b101 - assert pins.pin_actions == "VDV" - -def test_resetting_collection(): - c = origen.dut.pins.collect("portb", "p1") - c.set(0b0000) - c.capture() - assert c.data == 0 - assert c.pin_actions == "CCCC" - c.reset() - assert c.data == 0b1101 - assert c.pin_actions == "VDVD" - -def test_exception_on_invalid_reset_data(): - with pytest.raises(OSError): - origen.dut.add_pin("error", width=2, reset_data=0b111) - -def test_exception_on_invalid_reset_actions(): - with pytest.raises(OSError): - origen.dut.add_pin("error", width=2, reset_action="ZZZ") - with pytest.raises(OSError): - origen.dut.add_pin("error", width=2, reset_action="Z") - -def test_collecting_with_single_regex(): - import re - r = re.compile("port.0") - c = origen.dut.pins.collect(r) - assert c.pin_names == ["porta0", "portb0"] - -def test_collecting_using_ruby_like_syntax(): - c = origen.dut.pins.collect("/port.0/") - assert c.pin_names == ["porta0", "portb0"] - -def test_collecting_with_mixed_inputs(): - import re - r = re.compile("port.0") - c = origen.dut.pins.collect("/port.1/", "p1", r) - assert c.pin_names == ["porta1", "portb1", "p1", "porta0", "portb0"] - def test_physical_pin_has_empty_metadata(): assert origen.dut.physical_pin("porta0").added_metadata == [] diff --git a/rust/origen/src/core/model/pins.rs b/rust/origen/src/core/model/pins.rs index 0f338961..f8fa401a 100644 --- a/rust/origen/src/core/model/pins.rs +++ b/rust/origen/src/core/model/pins.rs @@ -30,8 +30,7 @@ impl Model { endianness: Option, ) -> Result<(PinGroup, Pin), Error> { let pin_group = PinGroup::new(self.id, pin_group_id, name.to_string(), vec!(name.to_string()), endianness); - let mut physical_pin = Pin::new(self.id, physical_pin_id, name.to_string(), reset_data, reset_action); - physical_pin.groups.insert(name.to_string(), 0); + let physical_pin = Pin::new(self.id, physical_pin_id, name.to_string(), reset_data, reset_action); self.pin_groups.insert(name.to_string(), pin_group_id); self.pins.insert(name.to_string(), physical_pin_id); Ok((pin_group, physical_pin)) @@ -231,27 +230,15 @@ impl Dut { { id = self.pin_groups.len(); } - let mut physical_names: Vec = vec!(); - for (i, pin_name) in pins.iter().enumerate() { - if let Some(p) = self.resolve_to_mut_physical_pin(model_id, pin_name) { - if physical_names.contains(&p.name) { - return Err(Error::new(&format!("Can not group pins under {} because pin (or an alias of) {} has already been added to the group!", name, p.name))); - } else { - p.groups.insert(String::from(name), i); - } - } else { - return Err(Error::new(&format!( - "Can not group pins under {} because pin {} does not exist!", - name, pin_name - ))); - } - if let Some(p) = self.get_pin_group(model_id, pin_name) { - physical_names.extend_from_slice(&p.pin_names); - } + let pnames = self.verify_names(model_id, &pins)?; + for (i, pname) in pnames.iter().enumerate() { + let p = self._get_mut_pin(model_id, pname)?; + p.groups.insert(String::from(name), i); } let model = &mut self.models[model_id]; - self.pin_groups.push(model.register_pin_group(id, name, physical_names, endianness)?); + //self.pin_groups.push(model.register_pin_group(id, name, physical_names, endianness)?); + self.pin_groups.push(model.register_pin_group(id, name, pnames, endianness)?); Ok(&self.pin_groups[id]) } diff --git a/rust/pyapi/src/pins.rs b/rust/pyapi/src/pins.rs index 7faad6f0..7ca43839 100644 --- a/rust/pyapi/src/pins.rs +++ b/rust/pyapi/src/pins.rs @@ -161,8 +161,18 @@ impl PyDUT { } None => {} } + let mut name_strs: Vec = vec![]; + for (_i, n) in pins.iter().enumerate() { + if n.get_type().name() == "re.Pattern" || n.get_type().name() == "_sre.SRE_Pattern" { + let r = n.getattr("pattern").unwrap(); + name_strs.push(format!("/{}/", r)); + } else { + let _n = n.extract::()?; + name_strs.push(_n.clone()); + } + } let mut dut = DUT.lock().unwrap(); - dut.group_pins_by_name(model_id, name, pins.extract()?, endianness)?; + dut.group_pins_by_name(model_id, name, name_strs, endianness)?; let gil = Python::acquire_gil(); let py = gil.python(); diff --git a/rust/pyapi/src/pins/pin.rs b/rust/pyapi/src/pins/pin.rs index 30efb42e..65109767 100644 --- a/rust/pyapi/src/pins/pin.rs +++ b/rust/pyapi/src/pins/pin.rs @@ -154,4 +154,11 @@ impl Pin { None => Ok(py.None()), } } + + #[getter] + fn get_groups(&self) -> PyResult> { + let dut = DUT.lock().unwrap(); + let pin = dut._get_pin(self.model_id, &self.name)?; + Ok(pin.groups.clone()) + } } From dc62b75d8b047598b6befa862fc3b69df30a07ee Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Sat, 22 Feb 2020 07:21:55 -0600 Subject: [PATCH 06/18] Moved pin group/collections to use list like API. Updated specs. Turns out the original pin group/collection list indexing wasn't actually aligned with Python. Should be now that its moved to list-like API --- example/tests/pin_api_test.py | 123 +++++++----------- rust/origen/src/core/model/pins/pin_group.rs | 2 +- rust/pyapi/src/pins/pin_collection.rs | 128 +++++++++---------- rust/pyapi/src/pins/pin_group.rs | 127 +++++++++--------- 4 files changed, 177 insertions(+), 203 deletions(-) diff --git a/example/tests/pin_api_test.py b/example/tests/pin_api_test.py index b0791b8f..bf6a514c 100644 --- a/example/tests/pin_api_test.py +++ b/example/tests/pin_api_test.py @@ -1,7 +1,7 @@ import pytest import origen, _origen # pylint: disable=import-error from tests.shared import clean_eagle, clean_falcon, clean_tester # pylint: disable=import-error -from tests.shared.python_like_apis import Fixture_DictLikeAPI # pylint: disable=import-error +from tests.shared.python_like_apis import Fixture_DictLikeAPI, Fixture_ListLikeAPI # pylint: disable=import-error class MyRandomClass: pass @@ -59,6 +59,52 @@ def boot_dict_under_test(self): dut.add_pin("p3") return dut.physical_pins +class TestPinGroupListLike(Fixture_ListLikeAPI): + def parameterize(self): + return { + "slice_klass": _origen.dut.pins.PinCollection + } + + def verify_i0(self, i): + assert isinstance(i, _origen.dut.pins.PinCollection) + assert i.pin_names == ["pins0"] + + def verify_i1(self, i): + assert isinstance(i, _origen.dut.pins.PinCollection) + assert i.pin_names == ["pins1"] + + def verify_i2(self, i): + assert isinstance(i, _origen.dut.pins.PinCollection) + assert i.pin_names == ["pins2"] + + def boot_list_under_test(self): + origen.app.instantiate_dut("dut.falcon") + origen.dut.add_pin("pins", width=3) + return origen.dut.pin("pins") + +class TestPinCollectionListLike(Fixture_ListLikeAPI): + def parameterize(self): + return { + "slice_klass": _origen.dut.pins.PinCollection + } + + def verify_i0(self, i): + assert isinstance(i, _origen.dut.pins.PinCollection) + assert i.pin_names == ["pins0"] + + def verify_i1(self, i): + assert isinstance(i, _origen.dut.pins.PinCollection) + assert i.pin_names == ["pins1"] + + def verify_i2(self, i): + assert isinstance(i, _origen.dut.pins.PinCollection) + assert i.pin_names == ["pins2"] + + def boot_list_under_test(self): + origen.app.instantiate_dut("dut.falcon") + origen.dut.add_pin("pins", width=3) + return origen.dut.pins.collect("pins0", "pins1", "pins2") + @pytest.fixture def ports(): origen.dut.add_pin("porta", width=4) @@ -742,81 +788,6 @@ def test_pin_loader_api(clean_eagle): assert origen.dut.pin("portc").reset_data == 0x3 assert origen.dut.pin("clk").reset_actions == "D" -def test_pin_group_list_like_api(clean_falcon, pins, grp, ports): - grp = origen.dut.pin("grp") - - # Check '__contains__' - assert "p1" in grp - - # Check '__getitem__' (indexing) - is_pin_collection(grp[0]) - assert grp[0].pin_names == ["p1"] - - # Check __len__ - assert len(grp) == 3 - - # Check iterating - # Note: Unlike the dictionary, this is ordered. - names = [["p1"], ["p2"], ["p3"]] - for i, name in enumerate(grp): - is_pin_collection(name) - assert name.pin_names == names[i] - - # Check 'to_list' - as_list = list(grp) - assert isinstance(as_list, list) - for i, item in enumerate(as_list): - is_pin_collection(item) - assert item.pin_names == names[i] - -def test_pin_collection_from_pin_group(): - origen.dut.group_pins("p", "p0", "p1", "p2", "p3") - grp = origen.dut.pins["p"] - - assert grp[0].pin_names == ["p0"] - assert grp[1].pin_names == ["p1"] - assert grp[0:1].pin_names == ["p0", "p1"] - assert grp[1:3:2].pin_names == ["p1", "p3"] - -def test_pin_collection_list_like_api(): - c = origen.dut.pins.collect("p0", "p1", "p2", "p3") - is_pin_collection(c) - - # Check __contains__ - assert "p0" in c - assert "p3" in c - - # Check __getitem__ (indexing) - assert c[0].pin_names == ["p0"] - assert c[1].pin_names == ["p1"] - - # Check Slicing - assert c[0:1].pin_names == ["p0", "p1"] - assert c[1:3:2].pin_names == ["p1", "p3"] - - # Check __len__ - assert len(c) == 4 - - # Check iterating - names = ["p0", "p1", "p2", "p3"] - for i, _c in enumerate(c): - is_pin_collection(_c) - _c.pin_names == list(names[i]) - -def test_exception_on_out_of_bounds_indexing(): - # Covers both pin collection and pin groups - grp = origen.dut.pin("grp") - with pytest.raises(OSError): - grp[100] - with pytest.raises(OSError): - grp[0:100] - - c = origen.dut.pins.collect("p1", "p2", "p3") - with pytest.raises(OSError): - c[100] - with pytest.raises(OSError): - c[0:100] - def test_physical_pin_has_empty_metadata(): assert origen.dut.physical_pin("porta0").added_metadata == [] diff --git a/rust/origen/src/core/model/pins/pin_group.rs b/rust/origen/src/core/model/pins/pin_group.rs index e474cdcb..826060d7 100644 --- a/rust/origen/src/core/model/pins/pin_group.rs +++ b/rust/origen/src/core/model/pins/pin_group.rs @@ -162,7 +162,7 @@ impl Dut { let names = &p.pin_names; let mut sliced_names: Vec = vec!(); - for i in (start_idx..=stop_idx).step_by(step_size) { + for i in (start_idx..stop_idx).step_by(step_size) { if i >= names.len() { return Err(Error::new(&format!( "Index {} exceeds available pins in group {} (length: {})", diff --git a/rust/pyapi/src/pins/pin_collection.rs b/rust/pyapi/src/pins/pin_collection.rs index 67e0309f..40fd0369 100644 --- a/rust/pyapi/src/pins/pin_collection.rs +++ b/rust/pyapi/src/pins/pin_collection.rs @@ -5,8 +5,10 @@ use origen::{lock, DUT}; use pyo3::prelude::*; #[allow(unused_imports)] use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PySlice, PyTuple}; +use super::super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; #[pyclass] +#[derive(Clone)] pub struct PinCollection { model_id: usize, pin_collection: OrigenPinCollection, @@ -134,12 +136,6 @@ impl PinCollection { Ok(dut.get_pin_collection_reset_actions(&self.pin_collection)?) } - // #[allow(non_snake_case)] - // #[getter] - // fn get__path(&self) -> PyResult { - // Ok(self.pin_collection.path.clone()) - // } - #[getter] fn get_big_endian(&self) -> PyResult { Ok(!self.pin_collection.is_little_endian()) @@ -153,51 +149,81 @@ impl PinCollection { #[pyproto] impl pyo3::class::sequence::PySequenceProtocol for PinCollection { - fn __len__(&self) -> PyResult { - Ok(self.pin_collection.len()) + // Need to overwrite contains to account for aliasing + fn __contains__(&self, item: &PyAny) -> PyResult { + if let Ok(s) = item.extract::() { + let dut = DUT.lock().unwrap(); + Ok(dut.pin_names_contain(self.model_id, &self.pin_collection.pin_names, &s)?) + } else { + Ok(false) + } } +} - fn __contains__(&self, item: &str) -> PyResult { - let dut = DUT.lock().unwrap(); - Ok(dut.pin_names_contain(self.model_id, &self.pin_collection.pin_names, item)?) +impl ListLikeAPI for PinCollection { + fn item_ids(&self, dut: &std::sync::MutexGuard) -> Vec { + let mut pin_ids: Vec = vec!(); + for pname in self.pin_collection.pin_names.iter() { + pin_ids.push(dut._get_pin(self.model_id, pname).unwrap().id); + } + pin_ids } -} -#[pyproto] -impl<'p> pyo3::class::PyMappingProtocol<'p> for PinCollection { - // Indexing example: https://github.com/PyO3/pyo3/blob/master/tests/test_dunder.rs#L423-L438 - fn __getitem__(&self, idx: &PyAny) -> PyResult { + // Grabs a single pin and puts it in an anonymous pin collection + fn new_pyitem(&self, py: Python, idx: usize) -> PyResult { + Ok(Py::new(py, + PinCollection::new(self.model_id, vec!(self.pin_collection.pin_names[idx].clone()), None)?) + .unwrap() + .to_object(py)) + } + + fn __iter__(&self) -> PyResult { + Ok(ListLikeIter { + parent: Box::new((*self).clone()), + i: 0, + }) + } + + fn ___getslice__(&self, slice: &PySlice) -> PyResult { + let mut names: Vec = vec!(); + { + let indices = slice.indices((self.pin_collection.pin_names.len() as i32).into())?; + let mut i = indices.start; + if indices.step > 0 { + while i < indices.stop { + names.push(self.pin_collection.pin_names[i as usize].clone()); + i += indices.step; + } + } else { + while i > indices.stop { + names.push(self.pin_collection.pin_names[i as usize].clone()); + i += indices.step; + } + } + } let gil = Python::acquire_gil(); let py = gil.python(); - if let Ok(slice) = idx.cast_as::() { - // Indices requires (what I think is) a max size. Should be plenty. - let indices = slice.indices(8192)?; - let collection = self.pin_collection.slice_names( - indices.start as usize, - indices.stop as usize, - indices.step as usize, - )?; - Ok(Py::new(py, PinCollection::from(collection)) + Ok(Py::new(py, PinCollection::new(self.model_id, names, None)?) .unwrap() .to_object(py)) - } else { - let i = idx.extract::().unwrap(); - let collection = self.pin_collection.slice_names(i as usize, i as usize, 1)?; - Ok(Py::new(py, PinCollection::from(collection)) - .unwrap() - .to_object(py)) - } + } +} + +#[pyproto] +impl pyo3::class::mapping::PyMappingProtocol for PinCollection { + fn __getitem__(&self, idx: &PyAny) -> PyResult { + ListLikeAPI::__getitem__(self, idx) + } + + fn __len__(&self) -> PyResult { + ListLikeAPI::__len__(self) } } #[pyproto] impl pyo3::class::iter::PyIterProtocol for PinCollection { - fn __iter__(slf: PyRefMut) -> PyResult { - Ok(PinCollectionIter { - keys: slf.pin_collection.pin_names.clone(), - i: 0, - model_id: slf.model_id, - }) + fn __iter__(slf: PyRefMut) -> PyResult { + ListLikeAPI::__iter__(&*slf) } } @@ -209,29 +235,3 @@ impl From for PinCollection { } } } - -#[pyclass] -pub struct PinCollectionIter { - keys: Vec, - i: usize, - model_id: usize, -} - -#[pyproto] -impl pyo3::class::iter::PyIterProtocol for PinCollectionIter { - fn __iter__(slf: PyRefMut) -> PyResult { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(slf.to_object(py)) - } - - fn __next__(mut slf: PyRefMut) -> PyResult> { - if slf.i >= slf.keys.len() { - return Ok(None); - } - let name = slf.keys[slf.i].clone(); - let collection = PinCollection::new(slf.model_id, vec![name], Option::None)?; - slf.i += 1; - Ok(Some(collection)) - } -} diff --git a/rust/pyapi/src/pins/pin_group.rs b/rust/pyapi/src/pins/pin_group.rs index df715083..749ffa1a 100644 --- a/rust/pyapi/src/pins/pin_group.rs +++ b/rust/pyapi/src/pins/pin_group.rs @@ -3,8 +3,10 @@ use origen::DUT; use pyo3::prelude::*; #[allow(unused_imports)] use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PySlice, PyTuple}; +use super::super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; #[pyclass] +#[derive(Clone)] pub struct PinGroup { pub name: String, pub model_id: usize, @@ -175,85 +177,86 @@ impl PinGroup { #[pyproto] impl pyo3::class::sequence::PySequenceProtocol for PinGroup { - fn __len__(&self) -> PyResult { - let dut = DUT.lock().unwrap(); - let grp = dut._get_pin_group(self.model_id, &self.name)?; - Ok(grp.len()) + // Need to overwrite contains to account for aliasing + fn __contains__(&self, item: &PyAny) -> PyResult { + if let Ok(s) = item.extract::() { + let dut = DUT.lock().unwrap(); + Ok(dut.pin_group_contains(self.model_id, &self.name, &s)?) + } else { + Ok(false) + } } +} - fn __contains__(&self, item: &str) -> PyResult { - let dut = DUT.lock().unwrap(); - Ok(dut.pin_group_contains(self.model_id, &self.name, item)?) +impl ListLikeAPI for PinGroup { + fn item_ids(&self, dut: &std::sync::MutexGuard) -> Vec { + let grp = dut._get_pin_group(self.model_id, &self.name).unwrap(); + let mut pin_ids: Vec = vec!(); + for pname in grp.pin_names.iter() { + pin_ids.push(dut._get_pin(self.model_id, pname).unwrap().id); + } + pin_ids } -} -#[pyproto] -impl<'p> pyo3::class::PyMappingProtocol<'p> for PinGroup { - // Indexing example: https://github.com/PyO3/pyo3/blob/master/tests/test_dunder.rs#L423-L438 - fn __getitem__(&self, idx: &PyAny) -> PyResult { + // Grabs a single pin and puts it in an anonymous pin collection + fn new_pyitem(&self, py: Python, idx: usize) -> PyResult { let dut = DUT.lock().unwrap(); + let collection = dut.slice_pin_group(self.model_id, &self.name, idx, idx+1, 1)?; + Ok(Py::new(py, PinCollection::from(collection)) + .unwrap() + .to_object(py)) + } + + fn __iter__(&self) -> PyResult { + Ok(ListLikeIter { + parent: Box::new((*self).clone()), + i: 0, + }) + } + fn ___getslice__(&self, slice: &PySlice) -> PyResult { + let mut names: Vec = vec!(); + { + let (indices, pin_names); + let dut = DUT.lock().unwrap(); + pin_names = &dut._get_pin_group(self.model_id, &self.name)?.pin_names; + indices = slice.indices((pin_names.len() as i32).into())?; + + let mut i = indices.start; + if indices.step > 0 { + while i < indices.stop { + names.push(pin_names[i as usize].clone()); + i += indices.step; + } + } else { + while i > indices.stop { + names.push(pin_names[i as usize].clone()); + i += indices.step; + } + } + } let gil = Python::acquire_gil(); let py = gil.python(); - if let Ok(slice) = idx.cast_as::() { - // Indices requires (what I think is) a max size. Should be plenty. - let indices = slice.indices(8192)?; - let collection = dut.slice_pin_group( - self.model_id, - &self.name, - indices.start as usize, - indices.stop as usize, - indices.step as usize, - )?; - Ok(Py::new(py, PinCollection::from(collection)) + Ok(Py::new(py, PinCollection::new(self.model_id, names, None)?) .unwrap() .to_object(py)) - } else { - let i = idx.extract::().unwrap(); - let collection = dut.slice_pin_group(self.model_id, &self.name, i as usize, i as usize, 1)?; - Ok(Py::new(py, PinCollection::from(collection)) - .unwrap() - .to_object(py)) - } } } #[pyproto] -impl pyo3::class::iter::PyIterProtocol for PinGroup { - fn __iter__(slf: PyRefMut) -> PyResult { - let dut = DUT.lock().unwrap(); - let grp = dut._get_pin_group(slf.model_id, &slf.name)?; - - Ok(PinGroupIter { - keys: grp.pin_names.clone(), - i: 0, - model_id: slf.model_id, - }) +impl pyo3::class::mapping::PyMappingProtocol for PinGroup { + fn __getitem__(&self, idx: &PyAny) -> PyResult { + ListLikeAPI::__getitem__(self, idx) } -} -#[pyclass] -pub struct PinGroupIter { - keys: Vec, - i: usize, - model_id: usize, + fn __len__(&self) -> PyResult { + ListLikeAPI::__len__(self) + } } #[pyproto] -impl pyo3::class::iter::PyIterProtocol for PinGroupIter { - fn __iter__(slf: PyRefMut) -> PyResult { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(slf.to_object(py)) - } - - fn __next__(mut slf: PyRefMut) -> PyResult> { - if slf.i >= slf.keys.len() { - return Ok(None); - } - let name = slf.keys[slf.i].clone(); - let collection = PinCollection::new(slf.model_id, vec![name], Option::None)?; - slf.i += 1; - Ok(Some(collection)) +impl pyo3::class::iter::PyIterProtocol for PinGroup { + fn __iter__(slf: PyRefMut) -> PyResult { + ListLikeAPI::__iter__(&*slf) } -} +} \ No newline at end of file From 5993628e5d30c58bae16453ca691ada2fc8600ae Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Sun, 23 Feb 2020 22:52:17 -0600 Subject: [PATCH 07/18] Added a rudimentary pin header object. Can be expanded later as needed. --- .../blocks/dut/derivatives/eagle/pins.py | 4 +- example/tests/pin_api_test.py | 92 +++++++++++++-- python/origen/pins.py | 17 +++ rust/origen/src/core/dut.rs | 10 +- rust/origen/src/core/model/mod.rs | 2 + rust/origen/src/core/model/pins.rs | 1 + rust/origen/src/core/model/pins/pin_header.rs | 55 +++++++++ rust/pyapi/src/pins.rs | 52 ++++++++ rust/pyapi/src/pins/pin_header.rs | 111 ++++++++++++++++++ 9 files changed, 331 insertions(+), 13 deletions(-) create mode 100644 rust/origen/src/core/model/pins/pin_header.rs create mode 100644 rust/pyapi/src/pins/pin_header.rs diff --git a/example/example/blocks/dut/derivatives/eagle/pins.py b/example/example/blocks/dut/derivatives/eagle/pins.py index 24b26240..5c42bbea 100644 --- a/example/example/blocks/dut/derivatives/eagle/pins.py +++ b/example/example/blocks/dut/derivatives/eagle/pins.py @@ -3,4 +3,6 @@ Pin("portb", width= 4) Pin("portc", width=2, reset_data=0b11) Pin("clk", reset_data=0, reset_action="D") -Alias("clk", "swd_clk", "tclk") \ No newline at end of file +Alias("clk", "swd_clk", "tclk") +PinHeader("ports", "porta", "portb", "portc") +PinHeader("clk", "clk") diff --git a/example/tests/pin_api_test.py b/example/tests/pin_api_test.py index bf6a514c..6f563212 100644 --- a/example/tests/pin_api_test.py +++ b/example/tests/pin_api_test.py @@ -778,17 +778,7 @@ def test_pins_in_subblocks(clean_falcon, pins): assert len(origen.dut.pins) == 4 assert origen.dut.pin("_p1") is None -def test_pin_loader_api(clean_eagle): - assert origen.dut.pins.keys() == [ - "porta0", "porta1", "porta", - "portb0", "portb1", "portb2", "portb3", "portb", - "portc0", "portc1", "portc", - "clk", "swd_clk", "tclk" - ] - assert origen.dut.pin("portc").reset_data == 0x3 - assert origen.dut.pin("clk").reset_actions == "D" - -def test_physical_pin_has_empty_metadata(): +def test_physical_pin_has_empty_metadata(clean_eagle): assert origen.dut.physical_pin("porta0").added_metadata == [] def test_adding_metadata_to_physical_pin(): @@ -847,3 +837,83 @@ def test_metadata_with_same_name_on_different_objects(): origen.dut.physical_pin('porta1').add_metadata("index", 1) assert origen.dut.physical_pin('porta0').get_metadata("index") == 0 assert origen.dut.physical_pin('porta1').get_metadata("index") == 1 + +class TestPinHeadersDictLikeAPI(Fixture_DictLikeAPI): + def parameterize(self): + return { + "keys": ["h0", "h1", "h2"], + "klass": _origen.dut.pins.PinHeader, + "not_in_dut": "Blah" + } + + def boot_dict_under_test(self): + origen.app.instantiate_dut("dut.falcon") + origen.dut.add_pin("p", width=3) + origen.dut.add_pin_header("h0", "p0") + origen.dut.add_pin_header("h1", "p0", "p1") + origen.dut.add_pin_header("h2", "p0", "p1", "p2") + return origen.dut.pin_headers + +class TestPinHeaders: + def test_empty_pin_headers(self, clean_falcon): + assert origen.dut.pin_headers.keys() == [] + + def test_adding_pin_headers(self, clean_falcon, pins): + h = origen.dut.add_pin_header("header", "p0", "p1", "p2") + assert isinstance(h, _origen.dut.pins.PinHeader) + assert h.pin_names == ["p0", "p1", "p2"] + + def test_adding_pin_headers_with_groups(self, clean_falcon, pins, ports): + h = origen.dut.add_pin_header("header", "p0", "portb") + assert isinstance(h, _origen.dut.pins.PinHeader) + assert h.pin_names == ["p0", "portb"] + assert h.physical_names == ["p0", "portb0", "portb1"] + assert len(h) == 3 + assert h.width == 3 + + def test_adding_pin_headers_with_aliases(self, clean_falcon, pins, ports): + origen.dut.add_pin_alias("portb", "pb") + h = origen.dut.add_pin_header("header", "p0", "pb") + assert isinstance(h, _origen.dut.pins.PinHeader) + assert h.pin_names == ["p0", "pb"] + assert h.physical_names == ["p0", "portb0", "portb1"] + assert len(h) == 3 + assert h.width == 3 + + def test_adding_pin_header_based_on_another(self, clean_falcon, pins, ports): + origen.dut.add_pin_header("header", "p0", "portb") + h = origen.dut.add_pin_header("header2", *origen.dut.pin_headers["header"].pin_names) + assert isinstance(h, _origen.dut.pins.PinHeader) + assert h.pin_names == ["p0", "portb"] + assert h.physical_names == ["p0", "portb0", "portb1"] + assert len(h) == 3 + assert h.width == 3 + + def test_exception_on_missing_pins(self, clean_falcon, pins): + assert "invalid" not in origen.dut.pin_headers + with pytest.raises(OSError): + origen.dut.add_pin_header("header", "p0", "missing") + assert "invalid" not in origen.dut.pin_headers + + def test_exception_on_duplicate_pins(self, clean_falcon, pins, ports): + assert "invalid" not in origen.dut.pin_headers + with pytest.raises(OSError): + origen.dut.add_pin_header("header", "p0", "p0") + with pytest.raises(OSError): + origen.dut.add_pin_header("header", "p0", "a0") + with pytest.raises(OSError): + origen.dut.add_pin_header("header", "portb0", "portb") + assert "invalid" not in origen.dut.pin_headers + +def test_pin_loader_api(clean_eagle): + assert origen.dut.pins.keys() == [ + "porta0", "porta1", "porta", + "portb0", "portb1", "portb2", "portb3", "portb", + "portc0", "portc1", "portc", + "clk", "swd_clk", "tclk" + ] + assert origen.dut.pin("portc").reset_data == 0x3 + assert origen.dut.pin("clk").reset_actions == "D" + assert origen.dut.pin_headers.keys() == ["ports", "clk"] + assert origen.dut.pin_headers["ports"].pin_names == ["porta", "portb", "portc"] + assert origen.dut.pin_headers["clk"].pin_names == ["clk"] diff --git a/python/origen/pins.py b/python/origen/pins.py index 74857159..6e4bca93 100644 --- a/python/origen/pins.py +++ b/python/origen/pins.py @@ -28,6 +28,16 @@ def group_pins(self, name, *pin_names, **options): def physical_pin(self, name): return origen.dut.db.physical_pin(self.controller.model_id, name) + @property + def pin_headers(self): + return origen.dut.db.pin_headers(self.controller.model_id) + + def add_pin_header(self, name, *pins): + return origen.dut.db.add_pin_header(self.controller.model_id, name, *pins) + + def pin_header(self, name): + return origen.dut.db.get_pin_header(self.controller.model_id, name) + @classmethod def api(cls): return [ @@ -38,6 +48,9 @@ def api(cls): 'group_pins', 'physical_pin', 'physical_pins', + 'pin_headers', + 'add_pin_header', + 'pin_header' ] class Loader: @@ -50,8 +63,12 @@ def Pin(self, name, **kwargs): def Alias(self, name, *aliases): self.controller.add_pin_alias(name, *aliases) + def PinHeader(self, name, *pins): + self.controller.add_pin_header(name, *pins) + def api(self): return { "Pin": self.Pin, "Alias": self.Alias, + "PinHeader": self.PinHeader, } diff --git a/rust/origen/src/core/dut.rs b/rust/origen/src/core/dut.rs index fec01898..82542ac3 100644 --- a/rust/origen/src/core/dut.rs +++ b/rust/origen/src/core/dut.rs @@ -4,6 +4,7 @@ use crate::core::model::registers::{ use crate::core::model::timesets::timeset::{Event, Timeset, Wave, WaveGroup, Wavetable}; use crate::core::model::pins::pin::{Pin}; use crate::core::model::pins::pin_group::{PinGroup}; +use crate::core::model::pins::pin_header::{PinHeader}; use crate::core::model::Model; use crate::error::Error; use crate::meta::IdGetters; @@ -20,7 +21,6 @@ use std::sync::RwLock; /// bit IDs. This approach allows bits to be easily passed around by ID to enable the creation of /// bit collections that are small (a subset of a register's bits) or very large (all bits in /// a memory map). -//#[include_id_getters] #[derive(Debug, IdGetters)] #[id_getters_by_mapping( field = "timeset", @@ -64,6 +64,12 @@ use std::sync::RwLock; return_type = "PinGroup", field_container_name = "pin_groups" )] +#[id_getters_by_mapping( + field = "pin_header", + parent_field = "models", + return_type = "PinHeader", + field_container_name = "pin_headers" +)] pub struct Dut { pub name: String, pub models: Vec, @@ -79,6 +85,7 @@ pub struct Dut { pub wave_events: Vec, pub pins: Vec, pub pin_groups: Vec, + pub pin_headers: Vec, pub id_mappings: Vec>, } @@ -102,6 +109,7 @@ impl Dut { wave_events: Vec::::new(), pins: Vec::::new(), pin_groups: Vec::::new(), + pin_headers: Vec::::new(), id_mappings: Vec::>::new(), } } diff --git a/rust/origen/src/core/model/mod.rs b/rust/origen/src/core/model/mod.rs index 300fff58..6a744f54 100644 --- a/rust/origen/src/core/model/mod.rs +++ b/rust/origen/src/core/model/mod.rs @@ -20,6 +20,7 @@ pub struct Model { // Pins pub pins: IndexMap, pub pin_groups: IndexMap, + pub pin_headers: IndexMap, pub timesets: IndexMap, // TODO: Levels @@ -52,6 +53,7 @@ impl Model { memory_maps: IndexMap::new(), pins: IndexMap::new(), pin_groups: IndexMap::new(), + pin_headers: IndexMap::new(), timesets: IndexMap::new(), address_unit_bits: 8, base_address: match base_address { diff --git a/rust/origen/src/core/model/pins.rs b/rust/origen/src/core/model/pins.rs index f8fa401a..d26c5d7b 100644 --- a/rust/origen/src/core/model/pins.rs +++ b/rust/origen/src/core/model/pins.rs @@ -1,6 +1,7 @@ pub mod pin; pub mod pin_collection; pub mod pin_group; +pub mod pin_header; use crate::error::Error; use std::convert::TryFrom; use super::super::dut::Dut; diff --git a/rust/origen/src/core/model/pins/pin_header.rs b/rust/origen/src/core/model/pins/pin_header.rs new file mode 100644 index 00000000..7952b6ef --- /dev/null +++ b/rust/origen/src/core/model/pins/pin_header.rs @@ -0,0 +1,55 @@ +use crate::error::Error; +use super::super::super::dut::Dut; +use super::super::super::model::Model; + +#[derive(Debug, Clone)] +pub struct PinHeader { + pub model_id: usize, + pub id: usize, + pub name: String, + pub pin_names: Vec, +} + +impl PinHeader { + pub fn new( + model_id: usize, + id: usize, + name: &str, + pins: Vec, + ) -> Self { + return Self { + model_id: model_id, + id: id, + name: String::from(name), + pin_names: pins, + }; + } +} + +impl Dut { + pub fn create_pin_header(&mut self, model_id: usize, name: &str, pins: Vec) -> Result<&PinHeader, Error> { + let id; + { + id = self.pin_headers.len(); + } + let _pnames = self.verify_names(model_id, &pins)?; + let model = &mut self.models[model_id]; + self.pin_headers.push(model.register_pin_header(id, name, pins)?); + Ok(&self.pin_headers[id]) + } +} + +impl Model { + pub fn register_pin_header(&mut self, pid: usize, name: &str, pin_names: Vec) -> Result { + let header = PinHeader::new(self.id, pid, name, pin_names); + self.pin_headers.insert(name.to_string(), pid); + Ok(header) + } + + pub fn get_pin_header_id(&self, name: &str) -> Option { + match self.pin_headers.get(name) { + Some(t) => Some(*t), + None => None, + } + } +} diff --git a/rust/pyapi/src/pins.rs b/rust/pyapi/src/pins.rs index 7ca43839..8151ad53 100644 --- a/rust/pyapi/src/pins.rs +++ b/rust/pyapi/src/pins.rs @@ -10,6 +10,7 @@ mod pin_group; mod pin_collection; mod physical_pin_container; mod pin_container; +mod pin_header; use origen::core::model::pins::Endianness; use physical_pin_container::PhysicalPinContainer; @@ -17,6 +18,7 @@ use pin::Pin; use pin_collection::PinCollection; use pin_container::PinContainer; use pin_group::PinGroup; +use pin_header::{PinHeader, PinHeaderContainer}; #[allow(unused_imports)] use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PyTuple}; @@ -28,6 +30,8 @@ pub fn pins(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; + m.add_class::()?; Ok(()) } @@ -217,4 +221,52 @@ impl PyDUT { None => Ok(py.None()), } } + + #[args(pins = "*")] + fn add_pin_header(&self, model_id: usize, name: &str, pins: &PyTuple) -> PyResult> { + let mut dut = DUT.lock().unwrap(); + dut.create_pin_header(model_id, name, pins.extract::>()?)?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(Py::new( + py, + PinHeader { + name: String::from(name), + model_id: model_id, + }, + ) + .unwrap()) + } + + fn pin_headers(&self, model_id: usize) -> PyResult> { + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(Py::new( + py, + PinHeaderContainer { + model_id: model_id, + }, + ) + .unwrap()) + } + + fn pin_header(&self, model_id: usize, name: &str) -> PyResult { + let dut = DUT.lock().unwrap(); + + let gil = Python::acquire_gil(); + let py = gil.python(); + match dut.get_pin_header(model_id, name) { + Some(_p) => Ok(Py::new( + py, + PinHeader { + name: String::from(name), + model_id: model_id, + }, + ) + .unwrap() + .to_object(py)), + None => Ok(py.None()), + } + } } diff --git a/rust/pyapi/src/pins/pin_header.rs b/rust/pyapi/src/pins/pin_header.rs new file mode 100644 index 00000000..bee37ab5 --- /dev/null +++ b/rust/pyapi/src/pins/pin_header.rs @@ -0,0 +1,111 @@ +use origen::DUT; +use pyo3::prelude::*; +use indexmap::map::IndexMap; +use crate::meta::py_like_apis::dict_like_api::{DictLikeAPI, DictLikeIter}; + +#[pyclass] +#[derive(Clone)] +pub struct PinHeader { + pub name: String, + pub model_id: usize, +} + +#[pymethods] +impl PinHeader { + #[getter] + fn get_name(&self) -> PyResult { + let dut = DUT.lock().unwrap(); + let h = dut._get_pin_header(self.model_id, &self.name)?; + Ok(h.name.clone()) + } + + #[getter] + fn pin_names(&self) -> PyResult> { + let dut = DUT.lock().unwrap(); + let h = dut._get_pin_header(self.model_id, &self.name)?; + Ok(h.pin_names.clone()) + } + + #[getter] + fn physical_names(&self) -> PyResult> { + let dut = DUT.lock().unwrap(); + let h = dut._get_pin_header(self.model_id, &self.name)?; + Ok(dut.verify_names(self.model_id, &h.pin_names)?) + } + + #[getter] + fn width(&self) -> PyResult { + Ok(self.physical_names()?.len()) + } +} + +#[pyproto] +impl pyo3::class::sequence::PySequenceProtocol for PinHeader { + // Need to overwrite contains to account for aliasing + fn __len__(&self) -> PyResult { + self.width() + } +} + +#[pyclass] +pub struct PinHeaderContainer { + pub model_id: usize, +} + +#[pymethods] +impl PinHeaderContainer { + fn keys(&self) -> PyResult> { + DictLikeAPI::keys(self) + } + + fn values(&self) -> PyResult> { + DictLikeAPI::values(self) + } + + fn items(&self) -> PyResult> { + DictLikeAPI::items(self) + } + + fn get(&self, name: &str) -> PyResult { + DictLikeAPI::get(self, name) + } +} + +impl DictLikeAPI for PinHeaderContainer { + fn lookup_key(&self) -> &str { + &"pin_headers" + } + + fn lookup_table( + &self, + dut: &std::sync::MutexGuard, + ) -> IndexMap { + dut.get_model(self.model_id).unwrap().pin_headers.clone() + } + + fn model_id(&self) -> usize { + self.model_id + } + + fn new_pyitem(&self, py: Python, name: &str, model_id: usize) -> PyResult { + Ok(Py::new(py, PinHeader {model_id: model_id, name: name.to_string()}).unwrap().to_object(py)) + } +} + +#[pyproto] +impl pyo3::class::mapping::PyMappingProtocol for PinHeaderContainer { + fn __getitem__(&self, name: &str) -> PyResult { + DictLikeAPI::__getitem__(self, name) + } + + fn __len__(&self) -> PyResult { + DictLikeAPI::__len__(self) + } +} + +#[pyproto] +impl pyo3::class::iter::PyIterProtocol for PinHeaderContainer { + fn __iter__(slf: PyRefMut) -> PyResult { + DictLikeAPI::__iter__(&*slf) + } +} From e181890d0e36ca7f9e4e254aa8b073cc91879f7e Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Mon, 16 Mar 2020 22:01:27 -0500 Subject: [PATCH 08/18] Base work for #88 - target system changes --- example/targets/{ => dut}/eagle.py | 0 example/targets/{ => dut}/falcon.py | 0 example/targets/eagle_with_simulator.py | 2 + example/targets/eagle_with_st7.py | 2 + example/targets/tester/sim.py | 1 + example/targets/tester/v93k_st7.py | 1 + python/origen/application.py | 2 +- python/origen/boot.py | 4 +- python/origen/target.py | 18 ++--- rust/origen/cli/src/bin.rs | 90 ++++++++++----------- rust/origen/cli/src/commands/environment.rs | 22 ----- rust/origen/cli/src/commands/interactive.rs | 4 +- rust/origen/cli/src/commands/mod.rs | 13 +-- rust/origen/cli/src/commands/target.rs | 20 ++--- rust/origen/src/core/application/target.rs | 4 + rust/origen/src/core/tester.rs | 18 +++++ rust/pyapi/src/tester.rs | 24 ++++++ 17 files changed, 122 insertions(+), 103 deletions(-) rename example/targets/{ => dut}/eagle.py (100%) rename example/targets/{ => dut}/falcon.py (100%) create mode 100644 example/targets/eagle_with_simulator.py create mode 100644 example/targets/eagle_with_st7.py create mode 100644 example/targets/tester/sim.py create mode 100644 example/targets/tester/v93k_st7.py delete mode 100644 rust/origen/cli/src/commands/environment.rs diff --git a/example/targets/eagle.py b/example/targets/dut/eagle.py similarity index 100% rename from example/targets/eagle.py rename to example/targets/dut/eagle.py diff --git a/example/targets/falcon.py b/example/targets/dut/falcon.py similarity index 100% rename from example/targets/falcon.py rename to example/targets/dut/falcon.py diff --git a/example/targets/eagle_with_simulator.py b/example/targets/eagle_with_simulator.py new file mode 100644 index 00000000..04c3a875 --- /dev/null +++ b/example/targets/eagle_with_simulator.py @@ -0,0 +1,2 @@ +origen.app.instantiate_dut("dut.eagle") +origen.tester.target("::Simulator") diff --git a/example/targets/eagle_with_st7.py b/example/targets/eagle_with_st7.py new file mode 100644 index 00000000..54e6044a --- /dev/null +++ b/example/targets/eagle_with_st7.py @@ -0,0 +1,2 @@ +origen.app.instantiate_dut("dut.eagle") +origen.tester.target("::V93K::ST7") diff --git a/example/targets/tester/sim.py b/example/targets/tester/sim.py new file mode 100644 index 00000000..8ca1fb6b --- /dev/null +++ b/example/targets/tester/sim.py @@ -0,0 +1 @@ +origen.tester.target("::Simulator") \ No newline at end of file diff --git a/example/targets/tester/v93k_st7.py b/example/targets/tester/v93k_st7.py new file mode 100644 index 00000000..72b60ce9 --- /dev/null +++ b/example/targets/tester/v93k_st7.py @@ -0,0 +1 @@ +origen.tester.target("::V93K::ST7") diff --git a/python/origen/application.py b/python/origen/application.py index 551f19d9..9a4b6ad5 100644 --- a/python/origen/application.py +++ b/python/origen/application.py @@ -40,7 +40,7 @@ def instantiate_dut(self, path): origen.dut = dut if origen.tester: # Clear the tester as it may hold references to a previous DUT's backend which was just wiped out. - origen.tester.reset() + origen.tester.clear_dut_dependencies() return dut # Instantiate the given block and return it diff --git a/python/origen/boot.py b/python/origen/boot.py index b0d1be6d..b9e549ad 100644 --- a/python/origen/boot.py +++ b/python/origen/boot.py @@ -89,7 +89,7 @@ def GetOutputFile(): # Called by the Origen CLI to boot the Origen Python env, not for application use # Any target/env overrides given to the command line will be passed in here -def __origen__(command, target=None, environment=None, mode=None, files=None): +def __origen__(command, targets=None, environment=None, mode=None, files=None): import origen import _origen import origen.application @@ -103,7 +103,7 @@ def __origen__(command, target=None, environment=None, mode=None, files=None): if files is not None: _origen.file_handler().init(files) - origen.target.load(target=target, environment=environment) + origen.target.load(targets=targets) if command == "generate": print("Generate command called!") diff --git a/python/origen/target.py b/python/origen/target.py index 3645832b..94752c70 100644 --- a/python/origen/target.py +++ b/python/origen/target.py @@ -2,16 +2,10 @@ import _origen # Load the target if one is currently set by the application -def load(target=None, environment=None): - app = origen.app - if target == None: - target = _origen.app_config()["target"] +def load(targets=None): + if targets == None: + targets = _origen.app_config()["target"] - if environment == None: - environment = _origen.app_config()["environment"] - - if target != None: - origen.load_file(_origen.target_file(target, "targets")) - - if environment != None: - origen.load_file(_origen.target_file(target, "environments")) + if targets != None: + for t in targets: + origen.load_file(_origen.target_file(t, "targets")) diff --git a/rust/origen/cli/src/bin.rs b/rust/origen/cli/src/bin.rs index ac78d6ac..88f81602 100644 --- a/rust/origen/cli/src/bin.rs +++ b/rust/origen/cli/src/bin.rs @@ -46,15 +46,11 @@ fn main() { .long("target") .help("Override the default target currently set by the workspace") .takes_value(true) + .use_delimiter(true) + .multiple(true) + .number_of_values(1) .value_name("TARGET") ) - .arg(Arg::with_name("environment") - .short("e") - .long("environment") - .help("Override the default environment currently set by the workspace") - .takes_value(true) - .value_name("ENVIRONMENT") - ) .arg(Arg::with_name("mode") .short("m") .long("mode") @@ -80,15 +76,11 @@ fn main() { .long("target") .help("Override the default target currently set by the workspace") .takes_value(true) + .use_delimiter(true) + .multiple(true) + .number_of_values(1) .value_name("TARGET") ) - .arg(Arg::with_name("environment") - .short("e") - .long("environment") - .help("Override the default environment currently set by the workspace") - .takes_value(true) - .value_name("ENVIRONMENT") - ) .arg(Arg::with_name("mode") .short("m") .long("mode") @@ -114,15 +106,11 @@ fn main() { .long("target") .help("Override the default target currently set by the workspace") .takes_value(true) + .use_delimiter(true) + .multiple(true) + .number_of_values(1) .value_name("TARGET") ) - .arg(Arg::with_name("environment") - .short("e") - .long("environment") - .help("Override the default environment currently set by the workspace") - .takes_value(true) - .value_name("ENVIRONMENT") - ) .arg(Arg::with_name("mode") .short("m") .long("mode") @@ -136,21 +124,25 @@ fn main() { .subcommand(SubCommand::with_name("target") .about("Set/view the default target") .visible_alias("t") - .arg(Arg::with_name("target") - .help("The name of the file from targets/ to be set as the default target") + .arg(Arg::with_name("targets") + .help("The name of the file(s) from targets/ to be set, added, or removed to the default target(s)") .takes_value(true) - .value_name("TARGET") + .value_name("TARGETS") + .multiple(true) ) - ) - - /************************************************************************************/ - .subcommand(SubCommand::with_name("environment") - .about("Set/view the default environment") - .visible_alias("e") - .arg(Arg::with_name("environment") - .help("The name of the file from environments/ to be set as the default environment") - .takes_value(true) - .value_name("ENVIRONMENT") + .arg(Arg::with_name("add") + .help("Adds the given target(s) to the current default target(s)") + .short("a") + .long("add") + .takes_value(false) + .conflicts_with("remove") + ) + .arg(Arg::with_name("remove") + .help("Removes the given target(s) to the current default target(s)") + .short("r") + .long("remove") + .takes_value(false) + .conflicts_with("add") ) ) @@ -181,8 +173,7 @@ fn main() { Some("interactive") => { let m = matches.subcommand_matches("interactive").unwrap(); commands::interactive::run( - &m.value_of("target"), - &m.value_of("environment"), + Some(m.values_of("target").unwrap().collect()), &m.value_of("mode"), ); } @@ -190,8 +181,7 @@ fn main() { let m = matches.subcommand_matches("generate").unwrap(); commands::launch( "generate", - &m.value_of("target"), - &m.value_of("environment"), + Some(m.values_of("target").unwrap().collect()), &m.value_of("mode"), Some(m.values_of("files").unwrap().collect()), ); @@ -200,19 +190,27 @@ fn main() { let m = matches.subcommand_matches("compile").unwrap(); commands::launch( "compile", - &m.value_of("target"), - &m.value_of("environment"), + Some(m.values_of("target").unwrap().collect()), &m.value_of("mode"), Some(m.values_of("files").unwrap().collect()), ); } Some("target") => { - let matches = matches.subcommand_matches("target").unwrap(); - commands::target::run(matches.value_of("target")); - } - Some("environment") => { - let matches = matches.subcommand_matches("environment").unwrap(); - commands::environment::run(matches.value_of("environment")); + let m = matches.subcommand_matches("target").unwrap(); + let a; + if m.value_of("add").is_some() { + a = Some("add"); + } else if m.value_of("remove").is_some() { + a = Some("remove"); + } else { + a = None; + } + //commands::target::run(matches.value_of("target")); + if let Some(targets) = m.values_of("targets") { + commands::target::run(Some(targets.collect()), a); + } else { + commands::target::run(None, a); + } } Some("mode") => { let matches = matches.subcommand_matches("mode").unwrap(); diff --git a/rust/origen/cli/src/commands/environment.rs b/rust/origen/cli/src/commands/environment.rs deleted file mode 100644 index 9b467df7..00000000 --- a/rust/origen/cli/src/commands/environment.rs +++ /dev/null @@ -1,22 +0,0 @@ -use origen::core::application::target; -use origen::APPLICATION_CONFIG; - -pub fn run(tname: Option<&str>) { - if tname.is_none() { - if APPLICATION_CONFIG.environment.is_some() { - let name = APPLICATION_CONFIG.environment.clone().unwrap(); - println!("{}", name); - } else { - println!("No default environment is currently enabled in this workspace"); - } - } else { - let name = tname.unwrap(); - if name == "default" { - target::delete_val("environment"); - } else { - let c = target::clean_name(name, "environments", false); - target::set_workspace("environment", &c); - println!("Your workspace environment is now set to: {}", c); - } - } -} diff --git a/rust/origen/cli/src/commands/interactive.rs b/rust/origen/cli/src/commands/interactive.rs index 7f504471..b4f7a862 100644 --- a/rust/origen/cli/src/commands/interactive.rs +++ b/rust/origen/cli/src/commands/interactive.rs @@ -1,7 +1,7 @@ use origen::STATUS; use std::fs; -pub fn run(target: &Option<&str>, environment: &Option<&str>, mode: &Option<&str>) { +pub fn run(targets: Option>, mode: &Option<&str>) { let dot_origen_dir = STATUS.root.join(".origen"); if !dot_origen_dir.exists() { let _ = fs::create_dir(&dot_origen_dir); @@ -14,5 +14,5 @@ pub fn run(target: &Option<&str>, environment: &Option<&str>, mode: &Option<&str .open(&history_file); } - super::launch("interactive", target, environment, mode, None); + super::launch("interactive", targets, mode, None); } diff --git a/rust/origen/cli/src/commands/mod.rs b/rust/origen/cli/src/commands/mod.rs index 76e43d84..cd05c063 100644 --- a/rust/origen/cli/src/commands/mod.rs +++ b/rust/origen/cli/src/commands/mod.rs @@ -1,4 +1,3 @@ -pub mod environment; pub mod interactive; pub mod mode; pub mod setup; @@ -11,8 +10,7 @@ use origen::clean_mode; /// Launch the given command in Python pub fn launch( command: &str, - target: &Option<&str>, - environment: &Option<&str>, + targets: Option>, mode: &Option<&str>, files: Option>, ) { @@ -21,12 +19,9 @@ pub fn launch( command ); - if target.is_some() { - cmd += &format!(", target='{}'", target.unwrap()).to_string(); - } - - if environment.is_some() { - cmd += &format!(", environment='{}'", environment.unwrap()).to_string(); + if let Some(t) = targets { + let _t: Vec = t.iter().map(|__t| format!("'{}'", __t)).collect(); + cmd += &format!(", targets=[{}]", &_t.join(",")).to_string(); } if mode.is_some() { diff --git a/rust/origen/cli/src/commands/target.rs b/rust/origen/cli/src/commands/target.rs index 9295bbb8..bc96fc33 100644 --- a/rust/origen/cli/src/commands/target.rs +++ b/rust/origen/cli/src/commands/target.rs @@ -1,8 +1,8 @@ use origen::core::application::target; use origen::APPLICATION_CONFIG; -pub fn run(tname: Option<&str>) { - if tname.is_none() { +pub fn run(tnames: Option>, action: Option<&str>) { + if tnames.is_none() { if APPLICATION_CONFIG.target.is_some() { let name = APPLICATION_CONFIG.target.clone().unwrap(); println!("{}", name); @@ -10,13 +10,15 @@ pub fn run(tname: Option<&str>) { println!("No default target is currently enabled in this workspace"); } } else { - let name = tname.unwrap(); - if name == "default" { - target::delete_val("target"); - } else { - let c = target::clean_name(name, "targets", false); - target::set_workspace("target", &c); - println!("Your workspace target is now set to: {}", c); + for name in tnames.unwrap().iter() { + //let name = tname.unwrap(); + if name == &"default" { + target::delete_val("target"); + } else { + let c = target::clean_name(name, "targets", false); + target::set_workspace("target", &c); + println!("Your workspace target is now set to: {}", c); + } } } } diff --git a/rust/origen/src/core/application/target.rs b/rust/origen/src/core/application/target.rs index 5dd22cb5..2d363785 100644 --- a/rust/origen/src/core/application/target.rs +++ b/rust/origen/src/core/application/target.rs @@ -81,6 +81,10 @@ pub fn matches(name: &str, dir: &str) -> Vec { } } } + // After collecting all the matches, if the size > 1 then filter again for exact matches + if files.len() > 1 { + files = files.into_iter().filter(|path| path.file_name().unwrap().to_str().unwrap() == &format!("{}.py", name)).collect(); + } files } diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs index 43fa5131..c79bb103 100644 --- a/rust/origen/src/core/tester.rs +++ b/rust/origen/src/core/tester.rs @@ -193,13 +193,31 @@ impl Tester { } pub fn reset(&mut self) -> Result<(), Error> { + self.clear_dut_dependencies()?; + self.reset_external_generators()?; + Ok(()) + } + + /// Clears all members which reference members on the current DUT. + pub fn clear_dut_dependencies(&mut self) -> Result<(), Error> { self.ast.reset(); self.current_timeset_id = Option::None; + Ok(()) + } + + // Resets the external generators. + // Also clears the targeted generators, as it may point to an external one that will be cleared. + pub fn reset_external_generators(&mut self) -> Result<(), Error> { self.target_generators.clear(); self.external_generators.clear(); Ok(()) } + pub fn reset_targets(&mut self) -> Result<(), Error> { + self.target_generators.clear(); + Ok(()) + } + pub fn register_external_generator(&mut self, generator: &str) -> Result<(), Error> { self.external_generators.insert(generator.to_string(), Generators::External(generator.to_string())); Ok(()) diff --git a/rust/pyapi/src/tester.rs b/rust/pyapi/src/tester.rs index ff4710f0..5ed67455 100644 --- a/rust/pyapi/src/tester.rs +++ b/rust/pyapi/src/tester.rs @@ -42,6 +42,30 @@ impl PyTester { Ok(slf.to_object(py)) } + fn clear_dut_dependencies(slf: PyRef) -> PyResult { + origen::tester().clear_dut_dependencies()?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + + fn reset_external_generators(slf: PyRef) -> PyResult { + origen::tester().reset_external_generators()?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + + fn reset_targets(slf: PyRef) -> PyResult { + origen::tester().reset_targets()?; + + let gil = Python::acquire_gil(); + let py = gil.python(); + Ok(slf.to_object(py)) + } + #[getter] fn get_timeset(&self) -> PyResult { let tester = origen::tester(); From 7101fd0cddf70ccc071b0f603f2bedde1d54368f Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Tue, 17 Mar 2020 07:55:08 -0500 Subject: [PATCH 09/18] Quick bugfix induced by starting #88 --- rust/origen/cli/src/bin.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/rust/origen/cli/src/bin.rs b/rust/origen/cli/src/bin.rs index 88f81602..d84aea99 100644 --- a/rust/origen/cli/src/bin.rs +++ b/rust/origen/cli/src/bin.rs @@ -173,7 +173,11 @@ fn main() { Some("interactive") => { let m = matches.subcommand_matches("interactive").unwrap(); commands::interactive::run( - Some(m.values_of("target").unwrap().collect()), + if let Some(targets) = m.values_of("target") { + Some(targets.collect()) + } else { + Option::None + }, &m.value_of("mode"), ); } @@ -181,7 +185,11 @@ fn main() { let m = matches.subcommand_matches("generate").unwrap(); commands::launch( "generate", - Some(m.values_of("target").unwrap().collect()), + if let Some(targets) = m.values_of("target") { + Some(targets.collect()) + } else { + Option::None + }, &m.value_of("mode"), Some(m.values_of("files").unwrap().collect()), ); @@ -190,7 +198,11 @@ fn main() { let m = matches.subcommand_matches("compile").unwrap(); commands::launch( "compile", - Some(m.values_of("target").unwrap().collect()), + if let Some(targets) = m.values_of("target") { + Some(targets.collect()) + } else { + Option::None + }, &m.value_of("mode"), Some(m.values_of("files").unwrap().collect()), ); From 0f09092d2075c7aeb6c38e027c24c0ec4dc7870e Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Thu, 19 Mar 2020 22:11:13 -0500 Subject: [PATCH 10/18] Integrate proper AST with tester --- example/tests/shared/__init__.py | 10 +- example/tests/tester_test.py | 198 +++++----- python/origen/generator/tester_api.py | 15 + .../core/model/registers/bit_collection.rs | 1 - rust/origen/src/core/tester.rs | 302 ++++++--------- rust/origen/src/generator/ast.rs | 10 +- rust/origen/src/generator/processor.rs | 8 + rust/origen/src/generator/test_manager.rs | 2 +- rust/origen/src/lib.rs | 18 +- rust/origen/src/services/jtag/service.rs | 1 - rust/origen/src/testers/mod.rs | 192 +++++++--- rust/origen/src/testers/simulator/mod.rs | 40 +- rust/origen/src/testers/v93k/mod.rs | 69 +++- rust/pyapi/src/pins/pin_collection.rs | 4 +- rust/pyapi/src/tester.rs | 355 ++---------------- rust/pyapi/src/timesets/timeset_container.rs | 2 - 16 files changed, 478 insertions(+), 749 deletions(-) create mode 100644 python/origen/generator/tester_api.py diff --git a/example/tests/shared/__init__.py b/example/tests/shared/__init__.py index 8f2753c9..d0959842 100644 --- a/example/tests/shared/__init__.py +++ b/example/tests/shared/__init__.py @@ -17,7 +17,13 @@ def clean_falcon(): def clean_tester(): assert origen.tester origen.tester.reset() + assert len(origen.test_ast()["children"]) == 0 assert origen.tester.targets == [] - assert len(origen.tester.ast) == 0 assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7", "::Simulator"] - assert origen.tester.timeset is None \ No newline at end of file + assert origen.tester.timeset is None + +def check_last_node_type(t): + assert origen.test_ast()["children"][-1]["attrs"][0] == t + +def get_last_node(): + return origen.test_ast()["children"][-1] diff --git a/example/tests/tester_test.py b/example/tests/tester_test.py index 5c547acc..df6eb161 100644 --- a/example/tests/tester_test.py +++ b/example/tests/tester_test.py @@ -1,86 +1,85 @@ import pytest import origen, _origen # pylint: disable=import-error -from tests.shared import clean_eagle, clean_tester # pylint: disable=import-error +from tests.shared import clean_eagle, clean_tester, check_last_node_type, get_last_node # pylint: disable=import-error from tests.shared.python_like_apis import Fixture_ListLikeAPI # pylint: disable=import-error +from origen.generator.tester_api import TesterAPI # pylint: disable=import-error +from origen.generator.processor import Return # pylint: disable=import-error # Test generator used to test the frontend <-> backend generator hooks -class PyTestGenerator: +class PyTestGenerator(TesterAPI): def __init__(self): - self.nodes = [] + TesterAPI.__init__(self) + self.i = 0 - def generate(self, ast): + def on_test(self, node): print("Printing StubPyAST to console...") - for i, n in enumerate(ast): - if n.fields["type"] == "node": - node_str = "Node" - elif n.fields["type"] == "comment": - node_str = f"Comment - Content: {n.fields['content']}" - elif n.fields["type"] == "vector": - node_str = f"Vector - Repeat: {n.fields['repeat']}" - else: - node_str = f"Error! Unknown type {n.fields['type']}" - print(f" PyTestGenerator: Node {i}: {node_str}") + return Return.process_children + + def on_comment(self, node): + node_str = f"Comment - Content: {node['attrs'][1][1]}" + print(f" PyTestGenerator: Node {self.i}: {node_str}") + self.i += 1 + return Return.unmodified + + def on_cycle(self, node): + node_str = f"Vector - Repeat: {node['attrs'][1][0]}" + print(f" PyTestGenerator: Node {self.i}: {node_str}") + self.i += 1 + return Return.unmodified # Test generator used to test the frontend <-> backend interceptor hooks -class PyTestGeneratorWithInterceptor: - def generate(self, ast): - print("Printing StubPyAST to console...") - for i, n in enumerate(ast): - if n.fields["type"] == "node": - node_str = "Node" - elif n.fields["type"] == "comment": - node_str = f"Comment - Content: {n.fields['content']}" - elif n.fields["type"] == "vector": - node_str = f"Vector - Repeat: {n.fields['repeat']}" - else: - node_str = f"Error! Unknown type {n.fields['type']}" - print(f" PyTestGeneratorWithInterceptor: Node {i}: {node_str}") - - def cc(self, ast): - ast[-1].set('content', f"Intercepted By PyTestGeneratorWithInterceptor: {ast[-1].fields['content']}") +class PyTestGeneratorWithInterceptor(TesterAPI): + def __init__(self): + TesterAPI.__init__(self) + self.i = 0 -# Test generator which ignores everything and uses the meta interface only. -# Not overly practical, but this should work nonetheless. -class PyTestMetaGenerator: - def generate(self, ast): + def on_test(self, node): print("Printing StubPyAST to console...") - for i, n in enumerate(ast): - print(f" PyTestMetaGenerator: {i}: {n.get_metadata('meta_gen_str')}") + return Return.process_children - def cycle(self, ast): - ast[-1].add_metadata('meta_gen_str', f"Meta Cycle: {ast[-1].fields['repeat']}") + def on_comment(self, node): + node_str = f"Comment - Content: {node['attrs'][1][1]}" + print(f" PyTestGeneratorWithInterceptor: Node {self.i}: {node_str}") + self.i += 1 + return Return.unmodified - def cc(self, ast): - ast[-1].add_metadata('meta_gen_str', f"Meta CC: {ast[-1].fields['content']}") + def on_cycle(self, node): + node_str = f"Vector - Repeat: {node['attrs'][1][0]}" + print(f" PyTestGeneratorWithInterceptor: Node {self.i}: {node_str}") + self.i += 1 + return Return.unmodified -class TestPyAST(Fixture_ListLikeAPI): + def cc(self, node): + node_str = f"Comment - Content: {node['attrs'][1][1]}" + print(f"Intercepted By PyTestGeneratorWithInterceptor: {node_str}") + #ast[-1].set('content', f"Intercepted By PyTestGeneratorWithInterceptor: {ast[-1].fields['content']}") - def verify_i0(self, i): - assert i.fields["type"] == "comment" - assert i.fields["content"] == "Start!" +# Test generator which ignores everything and uses the meta interface only. +# Not overly practical, but this should work nonetheless. +class PyTestMetaGenerator(TesterAPI): + def __init__(self): + TesterAPI.__init__(self) + self.nodes = [] - def verify_i1(self, i): - assert i.fields["type"] == "vector" - assert i.fields["repeat"] == 4 + def on_test(self, _node): + print("Printing StubPyAST to console...") + for i, n in enumerate(self.nodes): + print(f" PyTestMetaGenerator: {i}: {n}") + return Return.unmodified - def verify_i2(self, i): - assert i.fields["type"] == "comment" - assert i.fields["content"] == "End!" + def cycle(self, node): + self.nodes.append(f"Meta Cycle: {node['attrs'][1][0]}") + return Return.unmodified - def boot_list_under_test(self): - origen.app.instantiate_dut("dut.eagle") - origen.tester.set_timeset("simple") - origen.tester.cc("Start!") - origen.tester.repeat(4) - origen.tester.cc("End!") - return origen.tester.ast + def cc(self, node): + self.nodes.append(f"Meta CC: {node['attrs'][1][1]}") + return Return.unmodified def test_init_state(clean_eagle, clean_tester): # The 'clean_tester' fixture has a number of asserts itself, # but just in case those change unbeknownst to this method, double check the initial state here. assert origen.tester assert origen.tester.targets == [] - assert len(origen.tester.ast) == 0 assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7", "::Simulator"] assert origen.tester.timeset is None @@ -126,67 +125,32 @@ def test_exception_on_unknown_target(clean_eagle, clean_tester): with pytest.raises(OSError): origen.tester.target("blah") -def test_ast_retrieval(clean_eagle, clean_tester): - assert origen.tester.ast is not None - assert isinstance(origen.tester.ast, _origen.tester.StubPyAST) - assert len(origen.tester.ast) == 0 - class TestTesterAPI: def test_cycle(self, clean_eagle, clean_tester): + assert len(origen.test_ast()["children"]) == 0 origen.tester.set_timeset("simple") - assert len(origen.tester.ast) == 0 - assert origen.tester.ast.cycle_count == 0 - assert origen.tester.ast.vector_count == 0 + check_last_node_type("SetTimeset") origen.tester.cycle() - assert len(origen.tester.ast) == 1 - assert origen.tester.ast[0].fields["type"] == "vector" - assert origen.tester.ast[0].fields["repeat"] == 1 - assert origen.tester.ast.cycle_count == 1 - assert origen.tester.ast.vector_count == 1 + check_last_node_type("Cycle") def test_multiple_cycles(self, clean_eagle, clean_tester): origen.tester.set_timeset("simple") - assert len(origen.tester.ast) == 0 - assert origen.tester.ast.cycle_count == 0 - assert origen.tester.ast.vector_count == 0 origen.tester.cycle() origen.tester.cycle() origen.tester.cycle() - assert len(origen.tester.ast) == 3 - assert origen.tester.ast.cycle_count == 3 - assert origen.tester.ast.vector_count == 3 + assert len(origen.test_ast()["children"]) == 4 def test_cc(self, clean_eagle, clean_tester): origen.tester.set_timeset("simple") - assert len(origen.tester.ast) == 0 origen.tester.cc("Hello Tester!") - assert len(origen.tester.ast) == 1 - assert origen.tester.ast[0].fields["type"] == "comment" - assert origen.tester.ast[0].fields["content"] == "Hello Tester!" + check_last_node_type("Comment") def test_repeat(self, clean_eagle, clean_tester): origen.tester.set_timeset("simple") - assert len(origen.tester.ast) == 0 - assert origen.tester.ast.cycle_count == 0 - assert origen.tester.ast.vector_count == 0 origen.tester.repeat(10) - assert len(origen.tester.ast) == 1 - assert origen.tester.ast[0].fields["type"] == "vector" - assert origen.tester.ast[0].fields["repeat"] == 10 - assert origen.tester.ast.cycle_count == 10 - assert origen.tester.ast.vector_count == 1 - - def test_multiple_cycles_and_repeat(self, clean_eagle, clean_tester): - origen.tester.set_timeset("simple") - assert len(origen.tester.ast) == 0 - assert origen.tester.ast.cycle_count == 0 - assert origen.tester.ast.vector_count == 0 - origen.tester.cycle() - origen.tester.cycle(repeat=11) - origen.tester.cycle() - assert len(origen.tester.ast) == 3 - assert origen.tester.ast.cycle_count == 13 - assert origen.tester.ast.vector_count == 3 + n = get_last_node() + assert n["attrs"][0] == "Cycle" + assert n["attrs"][1][0] == 10 def test_adding_frontend_generator(clean_eagle, clean_tester): assert "tester_test.PyTestGenerator" not in origen.tester.generators @@ -251,9 +215,9 @@ def test_interceptors_on_backend(self, capfd, clean_eagle, clean_tester): "Vector intercepted by DummyGeneratorWithInterceptors!", "Comment intercepted by DummyGeneratorWithInterceptors!", "Printing StubAST to console...", - " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Pattern Start!", + " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Pattern Start!", " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Pattern End!", + " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" @@ -281,10 +245,12 @@ def test_generator_with_interceptors(self, capfd, clean_eagle, clean_tester): origen.tester.generate() out, err = capfd.readouterr() assert out == "\n".join([ + "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern Start!", + "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern End!", "Printing StubPyAST to console...", - " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Pattern Start!", + " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Pattern Start!", " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", - " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Pattern End!", + " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" @@ -314,17 +280,20 @@ def test_targeted_generator_ordering(capfd, clean_eagle, clean_tester): origen.tester.generate() out, err = capfd.readouterr() assert out == "\n".join([ + "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern Start!", "Comment intercepted by DummyGeneratorWithInterceptors!", "Vector intercepted by DummyGeneratorWithInterceptors!", + "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern End!", "Comment intercepted by DummyGeneratorWithInterceptors!", "Printing StubPyAST to console...", - " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern Start!", + " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Pattern Start!", " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", - " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern End!", + " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Pattern End!", + "" "Printing StubAST to console...", - " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern Start!", + " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Pattern Start!", " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Comment intercepted by DummyGeneratorWithInterceptors! Intercepted By PyTestGeneratorWithInterceptor: Pattern End!", + " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" @@ -339,16 +308,19 @@ def test_targeted_generator_reverse_ordering(capfd, clean_eagle, clean_tester): out, err = capfd.readouterr() assert out == "\n".join([ "Comment intercepted by DummyGeneratorWithInterceptors!", + "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern Start!", "Vector intercepted by DummyGeneratorWithInterceptors!", "Comment intercepted by DummyGeneratorWithInterceptors!", + "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern End!", "Printing StubAST to console...", - " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern Start!", + " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Pattern Start!", " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern End!", + " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Pattern End!", + "" "Printing StubPyAST to console...", - " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern Start!", + " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Pattern Start!", " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", - " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Intercepted By PyTestGeneratorWithInterceptor: Comment intercepted by DummyGeneratorWithInterceptors! Pattern End!", + " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" diff --git a/python/origen/generator/tester_api.py b/python/origen/generator/tester_api.py new file mode 100644 index 00000000..e5d15c6f --- /dev/null +++ b/python/origen/generator/tester_api.py @@ -0,0 +1,15 @@ +import origen # pylint: disable=import-error +from . import processor # pylint: disable=import-error,relative-beyond-top-level +import pickle + +class TesterAPI(processor.Processor): + def __init__(self): + processor.Processor.__init__(self) + + def generate(self): + return self.process(origen.test_ast()) + + def __origen__issue_callback__(self, func, node_bytes): + if hasattr(self, func): + node = pickle.loads(bytes(node_bytes)) + return getattr(self, func)(node) \ No newline at end of file diff --git a/rust/origen/src/core/model/registers/bit_collection.rs b/rust/origen/src/core/model/registers/bit_collection.rs index f02a8d68..62ebeb91 100644 --- a/rust/origen/src/core/model/registers/bit_collection.rs +++ b/rust/origen/src/core/model/registers/bit_collection.rs @@ -1,6 +1,5 @@ use super::{Bit, Field, Register}; use crate::core::model::registers::AccessType; -use crate::generator::ast::*; use crate::node; use crate::{Dut, Error, Result, TEST}; use num_bigint::BigUint; diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs index c79bb103..2c5af285 100644 --- a/rust/origen/src/core/tester.rs +++ b/rust/origen/src/core/tester.rs @@ -1,164 +1,60 @@ use crate::error::Error; use super::model::timesets::timeset::{Timeset}; use indexmap::IndexMap; -use crate::testers::v93k::Generator as V93KGen; -use crate::testers::simulator::Generator as Simulator; -use crate::testers::{DummyGeneratorWithInterceptors, DummyGenerator}; +use crate::testers::{instantiate_tester, available_testers}; use crate::core::dut::{Dut}; +use crate::generator::ast::{Attrs, Node}; +use crate::TEST; +use crate::node; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug)] pub enum Generators { - DummyGenerator, - DummyGeneratorWithInterceptors, - V93kSt7, - Simulator, - //J750, - //Uflex, + Internal(Box), External(String), } -impl Generators { - pub fn from_str(s: &str) -> Option { - match s { - "::DummyGenerator" => Some(Self::DummyGenerator), - "::DummyGeneratorWithInterceptors" => Some(Self::DummyGeneratorWithInterceptors), - "::V93K::ST7" => Some(Self::V93kSt7), - "::Simulator" => Some(Self::Simulator), - _ => None - } - } - - pub fn to_string(&self) -> String { +impl Clone for Generators { + fn clone(&self) -> Generators { match self { - Self::DummyGenerator => "::DummyGenerator".to_string(), - Self::DummyGeneratorWithInterceptors => "::DummyGeneratorWithInterceptors".to_string(), - Self::V93kSt7 => "::V93K::ST7".to_string(), - Self::Simulator => "::Simulator".to_string(), - Self::External(g) => g.clone(), + Generators::Internal(_g) => Generators::Internal((*_g).clone()), + Generators::External(_g) => Generators::External(_g.clone()), } } - - pub fn to_vector_strings(&self) -> Vec { - vec!("::DummyGenerator".to_string(), "::DummyGeneratorWithInterceptors".to_string(), "::V93K::ST7".to_string(), "::Simulator".to_string()) - } -} - -#[derive(Debug)] -pub struct ExternalGenerator { - name: String, - source: String, - generator: Box, -} - -#[derive(Debug)] -pub enum StubNodes { - Comment { - content: String, - meta: IndexMap, - }, - Vector { - //timeset: String, - timeset_id: usize, - repeat: usize, - meta: IndexMap, - }, - Node { - meta: IndexMap, - }, } -impl StubNodes { - pub fn add_metadata_id(&mut self, identifier: &str, id: usize) -> Result<(), Error> { - match self { - Self::Comment {content: _, meta} => { - meta.insert(identifier.to_string(), id); - }, - Self::Vector {timeset_id: _, repeat: _, meta} => { - meta.insert(identifier.to_string(), id); - }, - Self::Node {meta} => { - meta.insert(identifier.to_string(), id); +impl PartialEq for Generators { + fn eq(&self, g: &Generators) -> bool { + match g { + Generators::Internal(_g) => match self { + Generators::Internal(_self) => { + *_g.name() == *_self.name() + }, + _ => false + } + Generators::External(_g) => match self { + Generators::External(_self) => { + _g == _self + }, + _ => false } } - Ok(()) } +} - pub fn get_metadata_id(&self, identifier: &str) -> Option { +impl Generators { + pub fn to_string(&self) -> String { match self { - Self::Comment {content: _, meta} => { - match meta.get(identifier) { - Some(id) => Some(*id), - None => None, - } - }, - Self::Vector {timeset_id: _, repeat: _, meta} => { - match meta.get(identifier) { - Some(id) => Some(*id), - None => None, - } - }, - Self::Node {meta} => { - match meta.get(identifier) { - Some(id) => Some(*id), - None => None, - } - } + Self::External(g) => g.clone(), + Self::Internal(g) => g.to_string(), } } } #[derive(Debug)] -pub struct StubAST { - pub nodes: Vec, - vector_count: usize, - cycle_count: usize, -} - -impl StubAST { - pub fn new() -> Self { - Self { - nodes: vec!(), - vector_count: 0, - cycle_count: 0, - } - } - - pub fn reset(&mut self) -> () { - self.nodes.clear(); - self.vector_count = 0; - self.cycle_count = 0; - } - - pub fn push_comment(&mut self, comment: &str) -> Result<(), Error> { - self.nodes.push(StubNodes::Comment { - content: comment.to_string(), - meta: IndexMap::new(), - }); - Ok(()) - } - - pub fn push_vector(&mut self, timeset_id: usize, repeat: usize) -> Result<(), Error> { - self.nodes.push(StubNodes::Vector { - timeset_id: timeset_id, - repeat: repeat, - meta: IndexMap::new(), - }); - self.vector_count += 1; - self.cycle_count += repeat; - Ok(()) - } - - pub fn cycle_count(&self) -> usize { - self.cycle_count - } - - pub fn vector_count(&self) -> usize { - self.vector_count - } - - pub fn len(&self) -> usize { - self.nodes.len() - } +pub struct ExternalGenerator { + name: String, + source: String, + generator: Box, } #[derive(Debug)] @@ -167,10 +63,6 @@ pub struct Tester { /// This is the direct ID to the timeset object. /// The name and model ID can be found on this object. current_timeset_id: Option, - - /// Stubbed AST. Replace this with something else when it becomes available. - ast: StubAST, - external_generators: IndexMap, pub target_generators: Vec, } @@ -179,7 +71,6 @@ impl Tester { pub fn new() -> Self { Tester { current_timeset_id: Option::None, - ast: StubAST::new(), external_generators: IndexMap::new(), target_generators: vec!(), } @@ -200,7 +91,7 @@ impl Tester { /// Clears all members which reference members on the current DUT. pub fn clear_dut_dependencies(&mut self) -> Result<(), Error> { - self.ast.reset(); + TEST.start("ad-hoc"); self.current_timeset_id = Option::None; Ok(()) } @@ -241,6 +132,7 @@ impl Tester { pub fn set_timeset(&mut self, dut: &Dut, model_id: usize, timeset_name: &str) -> Result<(), Error> { self.current_timeset_id = Some(dut._get_timeset(model_id, timeset_name)?.id); + TEST.push(node!(SetTimeset, self.current_timeset_id.unwrap())); Ok(()) } @@ -249,75 +141,59 @@ impl Tester { Ok(()) } - pub fn issue_callback_at(&mut self, func: &str, idx: usize, dut: &Dut) -> Result<(), Error> { - let g = &self.target_generators[idx]; + pub fn issue_callback_at(&mut self, idx: usize) -> Result<(), Error> { + let g = &mut self.target_generators[idx]; + + // Grab the last node and immutably pass it to the interceptor match g { - Generators::DummyGeneratorWithInterceptors => { - let g_ = DummyGeneratorWithInterceptors{}; - if func == "cc" { - g_.cc(&mut self.ast)?; - } else if func == "cycle" { - g_.cycle(&mut self.ast)?; - }; - }, - Generators::Simulator => { - let g_ = Simulator{}; - match func { - "cc" => g_.cc(&mut self.ast, dut)?, - "cycle" => g_.cycle(&mut self.ast, dut)?, - "set_timeset" => g_.set_timeset(self.current_timeset_id, dut)?, - "clear_timeset" => g_.clear_timeset(dut)?, - _ => {}, // Simulator does not have callbacks for other functions + Generators::Internal(g_) => { + let last_node = TEST.get(0).unwrap(); + match &last_node.attrs { + Attrs::Cycle(repeat, compressable) => g_.cycle(*repeat, *compressable, &last_node)?, + Attrs::Comment(level, msg) => g_.cc(*level, &msg, &last_node)?, + Attrs::SetTimeset(timeset_id) => g_.set_timeset(*timeset_id, &last_node)?, + Attrs::ClearTimeset() => g_.clear_timeset(&last_node)?, + _ => {} } - } + }, _ => {} } Ok(()) } pub fn cc(&mut self, comment: &str) -> Result<(), Error> { - self.ast.push_comment(comment)?; + let comment_node = node!(Comment, 1, comment.to_string()); + TEST.push(comment_node); Ok(()) } - pub fn cycle(&mut self, repeat: Option) -> Result<(), Error>{ - self.ast.push_vector(self._current_timeset_id()?, repeat.unwrap_or(1))?; + pub fn cycle(&mut self, repeat: Option) -> Result<(), Error> { + let cycle_node = node!(Cycle, repeat.unwrap_or(1) as u32, true); + TEST.push(cycle_node); Ok(()) } /// Generates the output for the target at index i. /// Allows the frontend to call generators in a loop. - pub fn generate_target_at(&mut self, idx: usize, dut: &Dut) -> Result { + pub fn generate_target_at(&mut self, idx: usize) -> Result { let mut stat = GenerateStatus::new(); - let g = &self.target_generators[idx]; + let g = &mut self.target_generators[idx]; match g { - Generators::DummyGenerator => { - DummyGenerator{}.generate(&self.ast, dut)?; - stat.completed.push(Generators::DummyGenerator.to_string()) - }, - Generators::DummyGeneratorWithInterceptors => { - DummyGeneratorWithInterceptors{}.generate(&self.ast, dut)?; - stat.completed.push(Generators::DummyGeneratorWithInterceptors.to_string()) - } - Generators::V93kSt7 => { - V93KGen{}.generate(&self.ast, dut)?; - stat.completed.push(Generators::V93kSt7.to_string()) - } - Generators::Simulator => { - Simulator{}.generate(&self.ast, dut)?; - stat.completed.push(Generators::Simulator.to_string()) - } Generators::External(gen) => { stat.external.push(gen.to_string()); + }, + Generators::Internal(gen) => { + TEST.process(&mut |ast| gen.run(ast)); + stat.completed.push(gen.to_string()) } } Ok(stat) } - pub fn target(&mut self, generator: &str) -> Result<(), Error> { + pub fn target(&mut self, generator: &str) -> Result<&Generators, Error> { let g; - if let Some(_g) = Generators::from_str(generator) { - g = _g; + if let Some(_g) = instantiate_tester(generator) { + g = Generators::Internal(_g); } else if let Some(_g) = self.external_generators.get(generator) { g = (*_g).clone(); } else { @@ -328,7 +204,7 @@ impl Tester { Err(Error::new(&format!("Generator {} has already been targeted!", generator))) } else { self.target_generators.push(g); - Ok(()) + Ok(&self.target_generators.last().unwrap()) } } @@ -345,16 +221,8 @@ impl Tester { Ok(()) } - pub fn get_ast(&self) -> &StubAST { - &self.ast - } - - pub fn get_mut_ast(&mut self) -> &mut StubAST { - &mut self.ast - } - pub fn generators(&self) -> Vec { - let mut gens = Generators::DummyGenerator.to_vector_strings(); + let mut gens: Vec = available_testers(); gens.extend(self.external_generators.iter().map(|(n, _)| n.clone()).collect::>()); gens } @@ -372,4 +240,44 @@ impl GenerateStatus { external: vec!(), } } -} \ No newline at end of file +} + +/// Trait which allows Rust-side implemented testers to intercept generic calls +/// from the tester. +/// Each method will be given the resulting node after processing. +/// Note: the node given is only a clone of what will be stored in the AST. +pub trait Interceptor { + fn cycle(&mut self, _repeat: u32, _compressable: bool, _node: &Node) -> Result<(), Error> { + Ok(()) + } + + fn set_timeset(&mut self, _timeset_id: usize, _node: &Node) -> Result<(), Error> { + Ok(()) + } + + fn clear_timeset(&mut self, _node: &Node) -> Result<(), Error> { + Ok(()) + } + + fn cc(&mut self, _level: u8, _msg: &str, _node: &Node) -> Result<(), Error> { + Ok(()) + } +} +impl<'a, T> Interceptor for &'a T where T: TesterAPI {} +impl<'a, T> Interceptor for &'a mut T where T: TesterAPI {} + +pub trait TesterAPI: std::fmt::Debug + crate::generator::processor::Processor + Interceptor { + fn name(&self) -> String; + fn clone(&self) -> Box; + fn run(&mut self, node: &Node) -> Node; + + fn to_string(&self) -> String { + format!("::{}", self.name()) + } +} + +impl PartialEq for dyn TesterAPI { + fn eq(&self, g: &Generators) -> bool { + self.to_string() == g.to_string() + } +} diff --git a/rust/origen/src/generator/ast.rs b/rust/origen/src/generator/ast.rs index b60b3221..05a34b66 100644 --- a/rust/origen/src/generator/ast.rs +++ b/rust/origen/src/generator/ast.rs @@ -10,12 +10,12 @@ use std::fmt; macro_rules! node { ( $attr:ident, $( $x:expr ),* ) => { { - Node::new(Attrs::$attr($( $x ),*)) + crate::generator::ast::Node::new(crate::generator::ast::Attrs::$attr($( $x ),*)) } }; ( $attr:ident ) => { { - Node::new(Attrs::$attr) + crate::generator::ast::Node::new(crate::generator::ast::Attrs::$attr) } }; } @@ -30,6 +30,8 @@ pub enum Attrs { ////////////////////////////////////////////////////////////////////////////////////////////////////////// Test(String), Comment(u8, String), // level, msg + SetTimeset(usize), // Indicates both a set or change of the current timeset + ClearTimeset(), PinWrite(Id, u128), PinVerify(Id, u128), RegWrite(Id, BigUint, Option, Option), // reg_id, data, overlay_enable, overlay_str @@ -79,6 +81,8 @@ impl Node { // Call the dedicated handler for this node if it exists let r = match &self.attrs { Attrs::Test(name) => processor.on_test(&name, &self), + Attrs::SetTimeset(timeset_id) => processor.on_set_timeset(*timeset_id, &self), + Attrs::ClearTimeset() => processor.on_clear_timeset(&self), Attrs::Comment(level, msg) => processor.on_comment(*level, &msg, &self), Attrs::PinWrite(id, data) => processor.on_pin_write(*id, *data), Attrs::PinVerify(id, data) => processor.on_pin_verify(*id, *data), @@ -299,7 +303,7 @@ impl AST { self.nodes.push(node); } - pub fn process(&self, process_fn: &dyn Fn(&Node) -> Node) -> Node { + pub fn process(&self, process_fn: &mut dyn FnMut(&Node) -> Node) -> Node { if self.nodes.len() > 1 { let node = self.to_node(); process_fn(&node) diff --git a/rust/origen/src/generator/processor.rs b/rust/origen/src/generator/processor.rs index 8fffd7aa..93d60c66 100644 --- a/rust/origen/src/generator/processor.rs +++ b/rust/origen/src/generator/processor.rs @@ -140,4 +140,12 @@ pub trait Processor { fn on_flow(&mut self, _name: &str, _node: &Node) -> Return { Return::_Unimplemented } + + fn on_set_timeset(&mut self, _timeset_id: usize, _node: &Node) -> Return { + Return::_Unimplemented + } + + fn on_clear_timeset(&mut self, _node: &Node) -> Return { + Return::_Unimplemented + } } diff --git a/rust/origen/src/generator/test_manager.rs b/rust/origen/src/generator/test_manager.rs index 6cb387ea..831c8d9b 100644 --- a/rust/origen/src/generator/test_manager.rs +++ b/rust/origen/src/generator/test_manager.rs @@ -83,7 +83,7 @@ impl TestManager { format!("{}", ast) } - pub fn process(&self, process_fn: &dyn Fn(&Node) -> Node) -> Node { + pub fn process(&self, process_fn: &mut dyn FnMut(&Node) -> Node) -> Node { let ast = self.ast.read().unwrap(); ast.process(process_fn) } diff --git a/rust/origen/src/lib.rs b/rust/origen/src/lib.rs index 620de89f..75e80e51 100644 --- a/rust/origen/src/lib.rs +++ b/rust/origen/src/lib.rs @@ -80,28 +80,16 @@ pub enum Value<'a> { Data(BigUint, u32), // value, size } -#[macro_export] -macro_rules! lock { - () => { - match DUT.lock() { - Ok(dut) => Ok(dut), - Err(e) => Err(origen::error::Error::new(&format!( - "Could not attain DUT lock!" - ))), - } - }; -} - pub fn dut() -> MutexGuard<'static, Dut> { - DUT.lock().unwrap() + DUT.try_lock().expect("Backend Error: Unable to acquire DUT lock!") } pub fn tester() -> MutexGuard<'static, Tester> { - TESTER.lock().unwrap() + TESTER.try_lock().expect("Backend Error: Unable to acquire TESTER lock!") } pub fn services() -> MutexGuard<'static, Services> { - SERVICES.lock().unwrap() + SERVICES.try_lock().expect("Backend Error: Unable to acquire SERVICES lock!") } /// Sanitizes the given mode string and returns it, but will exit the process if it is invalid diff --git a/rust/origen/src/services/jtag/service.rs b/rust/origen/src/services/jtag/service.rs index a6d77198..e83d01c6 100644 --- a/rust/origen/src/services/jtag/service.rs +++ b/rust/origen/src/services/jtag/service.rs @@ -1,7 +1,6 @@ //! The service implements the public API exposed to Python and provides //! all state storage for a JTAG driver instance -use crate::generator::ast::*; use crate::node; use crate::{Result, Value, TEST}; diff --git a/rust/origen/src/testers/mod.rs b/rust/origen/src/testers/mod.rs index 9578f04e..e77f2472 100644 --- a/rust/origen/src/testers/mod.rs +++ b/rust/origen/src/testers/mod.rs @@ -1,77 +1,157 @@ pub mod v93k; pub mod simulator; -use crate::core::tester::{StubAST, StubNodes}; use crate::error::Error; -use crate::core::dut::{Dut}; +use crate::dut; +use crate::generator::processor::{Return, Processor}; +use crate::generator::ast::{Node}; +use crate::core::tester::{TesterAPI, Interceptor}; -pub struct DummyGenerator {} +pub fn available_testers() -> Vec { + vec![ + "::DummyGenerator".to_string(), + "::DummyGeneratorWithInterceptors".to_string(), + "::V93K::ST7".to_string(), + "::Simulator".to_string(), + ] +} + +pub fn instantiate_tester(g: &str) -> Option> { + match &g { + &"::DummyGenerator" => Some(Box::new(DummyGenerator::default())), + &"::DummyGeneratorWithInterceptors" => Some(Box::new(DummyGeneratorWithInterceptors::default())), + &"::V93K::ST7" => Some(Box::new(v93k::Generator::default())), + &"::Simulator" => Some(Box::new(simulator::Generator::default())), + _ => None + } +} -impl DummyGenerator { +#[derive(Debug, Clone)] +pub struct DummyGenerator { + count: usize, + current_timeset_id: Option, +} - /// A dummy generator which simply prints everything to the screen. - pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { - println!("Printing StubAST to console..."); - for (i, n) in ast.nodes.iter().enumerate() { - match n { - StubNodes::Comment {content, ..} => println!(" ::DummyGenerator Node {}: Comment - Content: {}", i, content), - StubNodes::Vector {timeset_id, repeat, ..} => { - let t = &dut.timesets[*timeset_id]; - println!(" ::DummyGenerator Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); - }, - StubNodes::Node { .. } => println!(" ::DummyGenerator Node {}: Node", i), - } +impl Default for DummyGenerator { + fn default() -> Self { + Self { + count: 0, + current_timeset_id: Option::None, } - Ok(()) } } -pub struct DummyGeneratorWithInterceptors {} +impl DummyGenerator {} +impl Interceptor for DummyGenerator {} +impl TesterAPI for DummyGenerator { + fn name(&self) -> String { + "DummyGenerator".to_string() + } + + fn clone(&self) -> Box { + Box::new(std::clone::Clone::clone(self)) + } -impl DummyGeneratorWithInterceptors { + fn run(&mut self, node: &Node) -> Node { + node.process(self).unwrap() + } +} - /// A dummy generator which simply prints everything to the screen. - pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { +impl Processor for DummyGenerator { + fn on_test(&mut self, _name: &str, _node: &Node) -> Return { + // Not counting the top node as a node. Only comments and cycles. println!("Printing StubAST to console..."); - for (i, n) in ast.nodes.iter().enumerate() { - match n { - StubNodes::Comment {content, ..} => println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", i, content), - StubNodes::Vector {timeset_id, repeat, ..} => { - let t = &dut.timesets[*timeset_id]; - println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", i, repeat, t.name); - }, - StubNodes::Node { .. } => println!(" ::DummyGeneratorWithInterceptors Node {}: Node", i), - } - } - Ok(()) + Return::ProcessChildren + } + + fn on_comment(&mut self, _level: u8, msg: &str, _node: &Node) -> Return { + println!(" ::DummyGenerator Node {}: Comment - Content: {}", self.count, msg); + self.count += 1; + Return::Unmodified } - pub fn cycle(&self, ast: &mut StubAST) -> Result<(), Error> { - let n = ast.nodes.last_mut().unwrap(); - match n { - StubNodes::Vector { .. } => { - println!("Vector intercepted by DummyGeneratorWithInterceptors!"); - Ok(()) - }, - _ => Err(Error::new(&format!("Error Intercepting Vector! Expected vector node!"))) - } + fn on_cycle(&mut self, repeat: u32, _compressable: bool, _node: &Node) -> Return { + let dut = dut(); + let t = &dut.timesets[self.current_timeset_id.unwrap()]; + println!(" ::DummyGenerator Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); + self.count += 1; + Return::Unmodified + } + + fn on_set_timeset(&mut self, timeset_id: usize, _node: &Node) -> Return { + self.current_timeset_id = Some(timeset_id); + Return::Unmodified + } +} + +#[derive(Debug, Clone)] +pub struct DummyGeneratorWithInterceptors { + count: usize, + current_timeset_id: Option, +} + +impl DummyGeneratorWithInterceptors {} + +impl TesterAPI for DummyGeneratorWithInterceptors { + fn name(&self) -> String { + "DummyGeneratorWithInterceptors".to_string() + } + + fn clone(&self) -> Box { + Box::new(std::clone::Clone::clone(self)) } - pub fn cc(&self, ast: &mut StubAST) -> Result<(), Error> { - let n = ast.nodes.last().unwrap(); - let n_; - match n { - StubNodes::Comment {content, meta} => { - println!("Comment intercepted by DummyGeneratorWithInterceptors!"); - n_ = StubNodes::Comment { - content: String::from(format!("Comment intercepted by DummyGeneratorWithInterceptors! {}", content.clone())), - meta: meta.clone(), - }; - }, - _ => return Err(Error::new(&format!("Error Intercepting Comment! Expected comment node!"))) + fn run(&mut self, node: &Node) -> Node { + //let mut slf = Self::default(); + node.process(self).unwrap() + //node.clone() + } +} + +impl Default for DummyGeneratorWithInterceptors { + fn default() -> Self { + Self { + count: 0, + current_timeset_id: Option::None, } - drop(n); - let i = ast.len() - 1; - ast.nodes[i] = n_; + } +} + +impl Interceptor for DummyGeneratorWithInterceptors { + fn cycle(&mut self, _repeat: u32, _compressable: bool, _node: &Node) -> Result<(), Error> { + println!("Vector intercepted by DummyGeneratorWithInterceptors!"); + Ok(()) + } + + fn cc(&mut self, _level: u8, _msg: &str, _node: &Node) -> Result<(), Error> { + println!("Comment intercepted by DummyGeneratorWithInterceptors!"); Ok(()) } } + +impl Processor for DummyGeneratorWithInterceptors { + + fn on_test(&mut self, _name: &str, _node: &Node) -> Return { + // Not counting the top node as a node. Only comments and cycles. + println!("Printing StubAST to console..."); + Return::ProcessChildren + } + + fn on_comment(&mut self, _level: u8, msg: &str, _node: &Node) -> Return { + println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", self.count, msg); + self.count += 1; + Return::Unmodified + } + + fn on_cycle(&mut self, repeat: u32, _compressable: bool, _node: &Node) -> Return { + let dut = dut(); + let t = &dut.timesets[self.current_timeset_id.unwrap()]; + println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); + self.count += 1; + Return::Unmodified + } + + fn on_set_timeset(&mut self, timeset_id: usize, _node: &Node) -> Return { + self.current_timeset_id = Some(timeset_id); + Return::Unmodified + } +} diff --git a/rust/origen/src/testers/simulator/mod.rs b/rust/origen/src/testers/simulator/mod.rs index 4bdab353..c24fd03f 100644 --- a/rust/origen/src/testers/simulator/mod.rs +++ b/rust/origen/src/testers/simulator/mod.rs @@ -1,32 +1,50 @@ -use crate::core::tester::{StubAST}; use crate::error::Error; -use crate::core::dut::{Dut}; +use crate::generator::processor::{Processor}; +use crate::generator::ast::{Node}; +use crate::core::tester::{TesterAPI, Interceptor}; +#[derive(Debug, Clone)] pub struct Generator {} -impl Generator { - /// The simulator's actions are done through the AST generation. There's actually nothing currently to do during generation. - pub fn generate(&self, _ast: &StubAST, _dut: &Dut) -> Result<(), Error> { - Ok(()) +impl Default for Generator { + fn default() -> Self { + Self {} } +} - pub fn clear_timeset(&self, _dut: &Dut) -> Result<(), Error> { +impl Processor for Generator {} +impl Interceptor for Generator { + fn clear_timeset(&mut self, _node: &Node) -> Result<(), Error> { println!(""); Ok(()) } - pub fn set_timeset(&self, _timeset_id: Option, _dut: &Dut) -> Result<(), Error> { + fn set_timeset(&mut self, _timeset_id: usize, _node: &Node) -> Result<(), Error> { println!(""); Ok(()) } - pub fn cycle(&self, _ast: &mut StubAST, _dut: &Dut) -> Result<(), Error> { + fn cycle(&mut self, _repeat: u32, _compressable: bool, _node: &Node) -> Result<(), Error> { println!(""); Ok(()) } - pub fn cc(&self, _ast: &mut StubAST, _dut: &Dut) -> Result<(), Error> { + fn cc(&mut self, _level: u8, _msg: &str, _node: &Node) -> Result<(), Error> { println!(""); Ok(()) } -} \ No newline at end of file + +} +impl TesterAPI for Generator { + fn name(&self) -> String { + "DummyGenerator".to_string() + } + + fn clone(&self) -> Box { + Box::new(std::clone::Clone::clone(self)) + } + + fn run(&mut self, node: &Node) -> Node { + node.process(self).unwrap() + } +} diff --git a/rust/origen/src/testers/v93k/mod.rs b/rust/origen/src/testers/v93k/mod.rs index afd5a2bd..1b2b5e5d 100644 --- a/rust/origen/src/testers/v93k/mod.rs +++ b/rust/origen/src/testers/v93k/mod.rs @@ -1,22 +1,51 @@ -use crate::core::tester::{StubAST, StubNodes}; -use crate::error::Error; -use crate::core::dut::{Dut}; - -pub struct Generator {} - -impl Generator { - /// An extremely elementary generator for the 93. Won't actually generate anything of use yet. - pub fn generate(&self, ast: &StubAST, dut: &Dut) -> Result<(), Error> { - for (_i, n) in ast.nodes.iter().enumerate() { - match n { - StubNodes::Comment {content, ..} => println!("# {}", content), - StubNodes::Vector {timeset_id, repeat, ..} => { - let t = &dut.timesets[*timeset_id]; - println!("R{} {} # ;", repeat, t.name); - }, - StubNodes::Node {..} => return Err(Error::new(&format!("Pure meta nodes are not supported by the V93K yet!"))), - } +use crate::DUT; +use crate::generator::processor::{Return, Processor}; +use crate::generator::ast::{Node}; +use crate::core::tester::{TesterAPI, Interceptor}; + +#[derive(Debug, Clone)] +pub struct Generator { + current_timeset_id: Option +} + +impl Default for Generator { + fn default() -> Self { + Self { + current_timeset_id: None } - Ok(()) } -} \ No newline at end of file +} + +impl Interceptor for Generator {} +impl TesterAPI for Generator { + fn name(&self) -> String { + "DummyGenerator".to_string() + } + + fn clone(&self) -> Box { + Box::new(std::clone::Clone::clone(self)) + } + + fn run(&mut self, node: &Node) -> Node { + node.process(self).unwrap() + } +} + +impl Processor for Generator { + fn on_comment(&mut self, _level: u8, msg: &str, _node: &Node) -> Return { + println!("# {}", msg); + Return::Unmodified + } + + fn on_cycle(&mut self, repeat: u32, _compressable: bool, _node: &Node) -> Return { + let dut = DUT.lock().unwrap(); + let t = &dut.timesets[self.current_timeset_id.unwrap()]; + println!("R{} {} # ;", repeat, t.name); + Return::Unmodified + } + + fn on_set_timeset(&mut self, timeset_id: usize, _node: &Node) -> Return { + self.current_timeset_id = Some(timeset_id); + Return::Unmodified + } +} diff --git a/rust/pyapi/src/pins/pin_collection.rs b/rust/pyapi/src/pins/pin_collection.rs index 40fd0369..49f6dcd4 100644 --- a/rust/pyapi/src/pins/pin_collection.rs +++ b/rust/pyapi/src/pins/pin_collection.rs @@ -1,7 +1,7 @@ use origen::core::model::pins::pin_collection::PinCollection as OrigenPinCollection; use origen::core::model::pins::Endianness; use origen::error::Error; -use origen::{lock, DUT}; +use origen::{dut, DUT}; use pyo3::prelude::*; #[allow(unused_imports)] use pyo3::types::{PyAny, PyBytes, PyDict, PyIterator, PyList, PySlice, PyTuple}; @@ -20,7 +20,7 @@ impl PinCollection { names: Vec, endianness: Option, ) -> Result { - let mut dut = lock!()?; + let mut dut = dut(); //let model = dut.get_mut_model(model_id)?; let collection = dut.collect(model_id, names, endianness)?; Ok(PinCollection { diff --git a/rust/pyapi/src/tester.rs b/rust/pyapi/src/tester.rs index 5ed67455..7edc3296 100644 --- a/rust/pyapi/src/tester.rs +++ b/rust/pyapi/src/tester.rs @@ -3,15 +3,12 @@ use pyo3::types::{PyAny, PyDict, PyTuple}; use super::timesets::timeset::{Timeset}; use std::collections::HashMap; use origen::error::Error; -use super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; -use origen::core::tester::{StubNodes, Generators}; -use pyo3::types::IntoPyDict; +use origen::core::tester::{Generators}; +use origen::TEST; #[pymodule] pub fn tester(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; - m.add_class::()?; - m.add_class::()?; Ok(()) } @@ -19,6 +16,7 @@ pub fn tester(_py: Python, m: &PyModule) -> PyResult<()> { #[derive(Debug)] pub struct PyTester { python_generators: HashMap, + instantiated_generators: HashMap, metadata: Vec, } @@ -29,6 +27,7 @@ impl PyTester { origen::tester().reset().unwrap(); obj.init({ PyTester { python_generators: HashMap::new(), + instantiated_generators: HashMap::new(), metadata: vec!(), } }); @@ -151,39 +150,26 @@ impl PyTester { match t { Generators::External(g) => { // External generators which the backend can't generate itself. Need to generate them here. - match self.python_generators.get(&(g.clone())) { - Some(gen) => { + match self.instantiated_generators.get(g) { + Some(inst) => { // The generator here is a PyObject - a handle on the class itself. // Instantiate it and call its generate method with the AST. let gil = Python::acquire_gil(); let py = gil.python(); - let inst = gen.call0(py)?; - let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); - - // Note: We could just try the callback on the generator and ignore an attribute error, but this ignores any attribute errors that - // may occur inside of the callback itself. So, check first if the attribute exists, so we know we're calling it. - let has_attr = inst.getattr(py, func); - - // the above attr either didn't throw an error or threw an attribute error, then carry on. - // Otherwise, something unexpected occured. Throw that error. - match has_attr { - Err(e) => { - if !e.is_instance::(py) { - return Err(PyErr::from(e)); - } - }, - _ => { - inst.call_method1(py, func, args)?; - }, - } + let last_node = TEST.get(0).unwrap().to_pickle(); + let args = PyTuple::new(py, &[func.to_object(py), last_node.to_object(py)]); + + // The issue callback function is located in origen.generator.tester_api.TesterAPI + // Easier to handle the actual calls there and since its all happening in the Python domain, doesn't really matter + // whether it happens here or there. + inst.call_method1(py, "__origen__issue_callback__", args)?; }, None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), } }, _ => { let mut tester = origen::tester(); - let dut = origen::dut(); - tester.issue_callback_at(func, i, &dut)?; + tester.issue_callback_at(i)?; } } } @@ -193,7 +179,6 @@ impl PyTester { /// Expecting more arguments/options to eventually be added here. #[args(kwargs="**")] fn cycle(slf: PyRef, kwargs: Option<&PyDict>) -> PyResult { - let targets; { let mut tester = origen::tester(); let mut repeat = None; @@ -203,42 +188,8 @@ impl PyTester { } } tester.cycle(repeat)?; - targets = tester.targets().clone(); - } - - // issue callbacks - for (i, t) in targets.iter().enumerate() { - match t { - Generators::External(g) => { - // External generators which the backend can't generate itself. Need to generate them here. - match slf.python_generators.get(&(g.clone())) { - Some(gen) => { - // The generator here is a PyObject - a handle on the class itself. - // Instantiate it and call its generate method with the AST. - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = gen.call0(py)?; - let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); - let r = inst.call_method1(py, "cycle", args); - match r { - Err(p) => { - if !p.is_instance::(py) { - return Err(PyErr::from(p)); - } - }, - _ => {}, - } - }, - None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), - } - }, - _ => { - let mut tester = origen::tester(); - let dut = origen::dut(); - tester.issue_callback_at("cycle", i, &dut)?; - } - } } + slf.issue_callbacks("cycle")?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -268,7 +219,7 @@ impl PyTester { } #[args(generators="*")] - fn target(&self, generators: &PyTuple) -> PyResult> { + fn target(&mut self, generators: &PyTuple) -> PyResult> { if generators.len() > 0 { let mut tester = origen::tester(); for g in generators.iter() { @@ -282,7 +233,15 @@ impl PyTester { let obj = g.to_object(py); let mut n = obj.getattr(py, "__module__")?.extract::(py)?; n.push_str(&format!(".{}", obj.getattr(py, "__qualname__")?.extract::(py)?)); - tester.target(&n)?; + let t = tester.target(&n)?; + match t { + Generators::External(gen) => { + let klass = self.python_generators.get(gen).unwrap(); + let inst = klass.call0(py)?; + self.instantiated_generators.insert(gen.to_string(), inst); + }, + _ => {} + } } } } @@ -314,23 +273,21 @@ impl PyTester { match t { Generators::External(g) => { // External generators which the backend can't generate itself. Need to generate them here. - match self.python_generators.get(g) { - Some(gen) => { + match self.instantiated_generators.get(g) { + Some(inst) => { // The generator here is a PyObject - a handle on the class itself. // Instantiate it and call its generate method with the AST. let gil = Python::acquire_gil(); let py = gil.python(); - let inst = gen.call0(py)?; - let args = PyTuple::new(py, &[Py::new(py, StubPyAST {})?.to_object(py)]); - inst.call_method1(py, "generate", args)?; + inst.call_method0(py, "generate")?; }, None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), } }, _ => { let mut tester = origen::tester(); - let dut = origen::DUT.lock().unwrap(); - tester.generate_target_at(i, &dut)?; + //let dut = origen::DUT.lock().unwrap(); + tester.generate_target_at(i)?; } } } @@ -342,256 +299,4 @@ impl PyTester { let tester = origen::tester(); Ok(tester.generators()) } - - #[getter] - fn ast(&self) -> PyResult { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(Py::new(py, StubPyAST {})?.to_object(py)) - } -} - -impl PyTester { - pub fn push_metadata(&mut self, item: &PyAny) -> usize { - let gil = Python::acquire_gil(); - let py = gil.python(); - - self.metadata.push(item.to_object(py)); - self.metadata.len() - 1 - } - - pub fn override_metadata_at(&mut self, idx: usize, item: &PyAny) -> PyResult<()> { - let gil = Python::acquire_gil(); - let py = gil.python(); - if self.metadata.len() > idx { - self.metadata[idx] = item.to_object(py); - Ok(()) - } else { - Err(PyErr::from(Error::new(&format!( - "Overriding metadata at {} exceeds the size of the current metadata vector!", - idx - )))) - } - } - - pub fn get_metadata(&self, idx: usize) -> PyResult<&PyObject> { - Ok(&self.metadata[idx]) - } -} - -#[pyclass] -#[derive(Debug, Clone)] -struct StubPyAST {} - -#[pymethods] -impl StubPyAST { - #[getter] - fn cycle_count(&self) -> PyResult { - let tester = origen::tester(); - let ast = tester.get_ast(); - - Ok(ast.cycle_count()) - } - - #[getter] - fn vector_count(&self) -> PyResult { - let tester = origen::tester(); - let ast = tester.get_ast(); - - Ok(ast.vector_count()) - } -} - -impl ListLikeAPI for StubPyAST { - fn item_ids(&self, _dut: &std::sync::MutexGuard) -> Vec { - // Todo: Turns this into a macro so we don't need the DUT. - let tester = origen::tester(); - let ast = tester.get_ast(); - - // The items ids won't actually be used here since this is structured differently than stuff on the DUT. - // For prototyping purposes, and to find opportunities for improvement, just hammering in screws here. - // All we really need from this is a vector that's the same size as the number of nodes in the AST. - let mut dummy = vec!(); - dummy.resize_with(ast.len(), || { 0 }); - dummy - } - - fn new_pyitem(&self, py: Python, idx: usize) -> PyResult { - let tester = origen::tester(); - let ast = tester.get_ast(); - let node = &ast.nodes[idx]; - let dict = PyDict::new(py); - match node { - StubNodes::Comment {content, ..} => { - dict.set_item("type", "comment")?; - dict.set_item("content", content)?; - }, - StubNodes::Vector {timeset_id, repeat, ..} => { - let (model_id, name); - { - let dut = origen::dut(); - let tset = &dut.timesets[*timeset_id]; - model_id = tset.model_id; - name = tset.name.clone(); - } - let t = Py::new(py, Timeset { - name: name, - model_id: model_id - }).unwrap().to_object(py); - - dict.set_item("timeset", t)?; - dict.set_item("type", "vector")?; - dict.set_item("repeat", repeat)?; - }, - StubNodes::Node {..} => { - dict.set_item("type", "node")?; - }, - } - let py_node = Py::new(py, PyNode::new(idx, dict.to_object(py))).unwrap(); - Ok(py_node.to_object(py)) - } - - fn __iter__(&self) -> PyResult { - Ok(ListLikeIter {parent: Box::new((*self).clone()), i: 0}) - } -} - -#[pyproto] -impl pyo3::class::mapping::PyMappingProtocol for StubPyAST { - fn __getitem__(&self, idx: &PyAny) -> PyResult { - ListLikeAPI::__getitem__(self, idx) - } - - fn __len__(&self) -> PyResult { - ListLikeAPI::__len__(self) - } -} - -#[pyproto] -impl pyo3::class::iter::PyIterProtocol for StubPyAST { - fn __iter__(slf: PyRefMut) -> PyResult { - ListLikeAPI::__iter__(&*slf) - } -} - -#[pyclass] -#[derive(Debug)] -pub struct PyNode { - pub fields: PyObject, - pub idx: usize, -} - -#[pymethods] -impl PyNode { - #[getter] - fn fields(&self) -> PyResult<&PyObject> { - Ok(&self.fields) - } -} - -#[pymethods] -impl PyNode { - fn add_metadata(&self, id_str: &str, obj: &PyAny) -> PyResult<()> { - let mut tester = origen::tester(); - let ast = tester.get_mut_ast(); - let node = &mut ast.nodes[self.idx]; - - let gil = Python::acquire_gil(); - let py = gil.python(); - let locals = [("origen", py.import("origen")?)].into_py_dict(py); - let pytester = py - .eval("origen.tester", None, Some(&locals)) - .unwrap() - .downcast_mut::()?; - let idx = pytester.push_metadata(obj); - - node.add_metadata_id(id_str, idx)?; - Ok(()) - } - - fn get_metadata(&self, id_str: &str) -> PyResult { - let tester = origen::tester(); - let ast = tester.get_ast(); - let node = &ast.nodes[self.idx]; - - let gil = Python::acquire_gil(); - let py = gil.python(); - match node.get_metadata_id(id_str) { - Some(idx) => { - let locals = [("origen", py.import("origen")?)].into_py_dict(py); - let pytester = py - .eval("origen.tester", None, Some(&locals)) - .unwrap() - .downcast_mut::()?; - let obj = pytester.get_metadata(idx)?; - Ok(obj.to_object(py)) - } - None => Ok(py.None()), - } - } - - fn set_metadata(&self, id_str: &str, obj: &PyAny) -> PyResult { - let mut tester = origen::tester(); - let ast = tester.get_mut_ast(); - let node = &mut ast.nodes[self.idx]; - - let gil = Python::acquire_gil(); - let py = gil.python(); - let locals = [("origen", py.import("origen")?)].into_py_dict(py); - let pytester = py - .eval("origen.tester", None, Some(&locals)) - .unwrap() - .downcast_mut::()?; - match node.get_metadata_id(id_str) { - Some(idx) => { - pytester.override_metadata_at(idx, obj)?; - Ok(true) - } - None => { - let idx = pytester.push_metadata(obj); - node.add_metadata_id(id_str, idx)?; - Ok(false) - } - } - } - - // This is more of just a prototype at this point to ensure things like this will work. - fn set(&self, field: &str, value: &PyAny) -> PyResult<()> { - let mut tester = origen::tester(); - let ast = tester.get_mut_ast(); - let node = &mut ast.nodes[self.idx]; - let node_; - - match node { - StubNodes::Comment {content: _, meta} => { - match field { - "content" => { - node_ = StubNodes::Comment { - content: value.extract::()?, - meta: meta.clone(), - }; - }, - _ => return Err(PyErr::from(Error::new(&format!("Node type 'comment' does not have field '{}'", field)))) - } - }, - StubNodes::Vector {timeset_id: _, repeat: _, meta: _} => { - return Err(PyErr::from(Error::new(&format!("Node type 'vector' does not have field '{}'", field)))) - }, - StubNodes::Node {..} => { - return Err(PyErr::from(Error::new(&format!("Node type 'node' does not have field '{}'", field)))) - }, - } - drop(node); - ast.nodes[self.idx] = node_; - Ok(()) - } -} - -impl PyNode { - pub fn new(idx: usize, dict: PyObject) -> Self { - Self { - fields: dict, - idx: idx, - } - } } diff --git a/rust/pyapi/src/timesets/timeset_container.rs b/rust/pyapi/src/timesets/timeset_container.rs index b4092e6c..31c01e23 100644 --- a/rust/pyapi/src/timesets/timeset_container.rs +++ b/rust/pyapi/src/timesets/timeset_container.rs @@ -4,8 +4,6 @@ use super::super::meta::py_like_apis::dict_like_api::{DictLikeAPI, DictLikeIter} use super::super::meta::py_like_apis::list_like_api::{ListLikeAPI, ListLikeIter}; use super::super::timesets::*; use indexmap::map::IndexMap; -use origen::error::Error; -use pyo3::class::mapping::*; //use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; From 02d3e0814acb676bc69b7cada465b6caf3b939e2 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Thu, 19 Mar 2020 22:44:57 -0500 Subject: [PATCH 11/18] Fix cargo tests --- rust/origen/src/generator/mod.rs | 2 +- rust/origen/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/origen/src/generator/mod.rs b/rust/origen/src/generator/mod.rs index 94c1fe46..fd192f4f 100644 --- a/rust/origen/src/generator/mod.rs +++ b/rust/origen/src/generator/mod.rs @@ -79,7 +79,7 @@ mod tests { // Test upcase comments processor - let new_ast = test.process(&|ast| UpcaseComments::run(ast)); + let new_ast = test.process(&mut |ast| UpcaseComments::run(ast)); let mut ast = AST::new(node!(Test, "trim_vbgap".to_string())); ast.push(node!(Comment, 1, "HELLO".to_string())); diff --git a/rust/origen/src/lib.rs b/rust/origen/src/lib.rs index 75e80e51..c7445594 100644 --- a/rust/origen/src/lib.rs +++ b/rust/origen/src/lib.rs @@ -81,15 +81,15 @@ pub enum Value<'a> { } pub fn dut() -> MutexGuard<'static, Dut> { - DUT.try_lock().expect("Backend Error: Unable to acquire DUT lock!") + DUT.lock().unwrap() } pub fn tester() -> MutexGuard<'static, Tester> { - TESTER.try_lock().expect("Backend Error: Unable to acquire TESTER lock!") + TESTER.lock().unwrap() } pub fn services() -> MutexGuard<'static, Services> { - SERVICES.try_lock().expect("Backend Error: Unable to acquire SERVICES lock!") + SERVICES.lock().unwrap() } /// Sanitizes the given mode string and returns it, but will exit the process if it is invalid From 3854bd49056e8bd96511fb036d952b6b73f866c4 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Fri, 20 Mar 2020 08:48:13 -0500 Subject: [PATCH 12/18] Round two of cargo test --- python/origen/generator/tester_api.py | 3 +++ .../src/core/model/registers/bit_collection.rs | 8 ++++---- rust/origen/src/lib.rs | 12 ++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/python/origen/generator/tester_api.py b/python/origen/generator/tester_api.py index e5d15c6f..318c2134 100644 --- a/python/origen/generator/tester_api.py +++ b/python/origen/generator/tester_api.py @@ -3,6 +3,9 @@ import pickle class TesterAPI(processor.Processor): + # Needed so pytest doesn't attempt to collect this as a test structure (since it starts with 'Test') + __test__ = False + def __init__(self): processor.Processor.__init__(self) diff --git a/rust/origen/src/core/model/registers/bit_collection.rs b/rust/origen/src/core/model/registers/bit_collection.rs index 62ebeb91..4c6986f1 100644 --- a/rust/origen/src/core/model/registers/bit_collection.rs +++ b/rust/origen/src/core/model/registers/bit_collection.rs @@ -701,7 +701,7 @@ impl<'a> BitCollection<'a> { #[cfg(test)] mod tests { use crate::core::model::registers::{Bit, BitCollection}; - use crate::{dut, Dut}; + use crate::{dut_or_wait, Dut}; use num_bigint::ToBigUint; use std::sync::MutexGuard; @@ -730,7 +730,7 @@ mod tests { #[test] fn data_method_works() { - let mut dut = dut(); + let mut dut = dut_or_wait(); let bc = make_bit_collection(16, &mut dut); assert_eq!(bc.data().unwrap(), 0.to_biguint().unwrap()); @@ -738,7 +738,7 @@ mod tests { #[test] fn set_data_method_works() { - let mut dut = dut(); + let mut dut = dut_or_wait(); let bc = make_bit_collection(16, &mut dut); bc.set_data(0.to_biguint().unwrap()); @@ -751,7 +751,7 @@ mod tests { #[test] fn range_method_works() { - let mut dut = dut(); + let mut dut = dut_or_wait(); let bc = make_bit_collection(16, &mut dut); bc.set_data(0x1234.to_biguint().unwrap()); diff --git a/rust/origen/src/lib.rs b/rust/origen/src/lib.rs index c7445594..6a061885 100644 --- a/rust/origen/src/lib.rs +++ b/rust/origen/src/lib.rs @@ -81,14 +81,26 @@ pub enum Value<'a> { } pub fn dut() -> MutexGuard<'static, Dut> { + DUT.try_lock().expect("Backend Error: Unable to acquire DUT lock!") +} + +pub fn dut_or_wait() -> MutexGuard<'static, Dut> { DUT.lock().unwrap() } pub fn tester() -> MutexGuard<'static, Tester> { + TESTER.try_lock().expect("Backend Error: Unable to acquire TESTER lock!") +} + +pub fn tester_or_wait() -> MutexGuard<'static, Tester> { TESTER.lock().unwrap() } pub fn services() -> MutexGuard<'static, Services> { + SERVICES.try_lock().expect("Backend Error: Unable to acquire SERVICESs lock!") +} + +pub fn services_or_wait() -> MutexGuard<'static, Services> { SERVICES.lock().unwrap() } From 8d7a11b8cf0d5f13eae1242a832d690161d46939 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Fri, 20 Mar 2020 17:08:00 -0500 Subject: [PATCH 13/18] Switched 'generator' to either 'tester' or 'renderer' depending on context --- example/tests/shared/__init__.py | 2 +- example/tests/tester_test.py | 203 +++++++++++------------ python/origen/generator/tester_api.py | 2 +- rust/origen/src/core/tester.rs | 113 ++++++------- rust/origen/src/testers/mod.rs | 52 +++--- rust/origen/src/testers/simulator/mod.rs | 12 +- rust/origen/src/testers/v93k/mod.rs | 12 +- rust/pyapi/src/tester.rs | 72 ++++---- 8 files changed, 230 insertions(+), 238 deletions(-) diff --git a/example/tests/shared/__init__.py b/example/tests/shared/__init__.py index d0959842..36d963a6 100644 --- a/example/tests/shared/__init__.py +++ b/example/tests/shared/__init__.py @@ -19,7 +19,7 @@ def clean_tester(): origen.tester.reset() assert len(origen.test_ast()["children"]) == 0 assert origen.tester.targets == [] - assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7", "::Simulator"] + assert origen.tester.testers == ["::DummyRenderer", "::DummyRendererWithInterceptors", "::V93K::ST7", "::Simulator"] assert origen.tester.timeset is None def check_last_node_type(t): diff --git a/example/tests/tester_test.py b/example/tests/tester_test.py index df6eb161..25679533 100644 --- a/example/tests/tester_test.py +++ b/example/tests/tester_test.py @@ -5,8 +5,8 @@ from origen.generator.tester_api import TesterAPI # pylint: disable=import-error from origen.generator.processor import Return # pylint: disable=import-error -# Test generator used to test the frontend <-> backend generator hooks -class PyTestGenerator(TesterAPI): +# Test tester used to test the frontend <-> backend tester hooks +class PyTestRenderer(TesterAPI): def __init__(self): TesterAPI.__init__(self) self.i = 0 @@ -17,18 +17,18 @@ def on_test(self, node): def on_comment(self, node): node_str = f"Comment - Content: {node['attrs'][1][1]}" - print(f" PyTestGenerator: Node {self.i}: {node_str}") + print(f" PyTestRenderer: Node {self.i}: {node_str}") self.i += 1 return Return.unmodified def on_cycle(self, node): node_str = f"Vector - Repeat: {node['attrs'][1][0]}" - print(f" PyTestGenerator: Node {self.i}: {node_str}") + print(f" PyTestRenderer: Node {self.i}: {node_str}") self.i += 1 return Return.unmodified -# Test generator used to test the frontend <-> backend interceptor hooks -class PyTestGeneratorWithInterceptor(TesterAPI): +# Test tester used to test the frontend <-> backend interceptor hooks +class PyTestRendererWithInterceptor(TesterAPI): def __init__(self): TesterAPI.__init__(self) self.i = 0 @@ -39,24 +39,23 @@ def on_test(self, node): def on_comment(self, node): node_str = f"Comment - Content: {node['attrs'][1][1]}" - print(f" PyTestGeneratorWithInterceptor: Node {self.i}: {node_str}") + print(f" PyTestRendererWithInterceptor: Node {self.i}: {node_str}") self.i += 1 return Return.unmodified def on_cycle(self, node): node_str = f"Vector - Repeat: {node['attrs'][1][0]}" - print(f" PyTestGeneratorWithInterceptor: Node {self.i}: {node_str}") + print(f" PyTestRendererWithInterceptor: Node {self.i}: {node_str}") self.i += 1 return Return.unmodified def cc(self, node): node_str = f"Comment - Content: {node['attrs'][1][1]}" - print(f"Intercepted By PyTestGeneratorWithInterceptor: {node_str}") - #ast[-1].set('content', f"Intercepted By PyTestGeneratorWithInterceptor: {ast[-1].fields['content']}") + print(f"Intercepted By PyTestRendererWithInterceptor: {node_str}") -# Test generator which ignores everything and uses the meta interface only. +# Test tester which ignores everything and uses the meta interface only. # Not overly practical, but this should work nonetheless. -class PyTestMetaGenerator(TesterAPI): +class PyTestMetaRenderer(TesterAPI): def __init__(self): TesterAPI.__init__(self) self.nodes = [] @@ -64,7 +63,7 @@ def __init__(self): def on_test(self, _node): print("Printing StubPyAST to console...") for i, n in enumerate(self.nodes): - print(f" PyTestMetaGenerator: {i}: {n}") + print(f" PyTestMetaRenderer: {i}: {n}") return Return.unmodified def cycle(self, node): @@ -80,7 +79,7 @@ def test_init_state(clean_eagle, clean_tester): # but just in case those change unbeknownst to this method, double check the initial state here. assert origen.tester assert origen.tester.targets == [] - assert origen.tester.generators == ["::DummyGenerator", "::DummyGeneratorWithInterceptors", "::V93K::ST7", "::Simulator"] + assert origen.tester.testers == ["::DummyRenderer", "::DummyRendererWithInterceptors", "::V93K::ST7", "::Simulator"] assert origen.tester.timeset is None def test_setting_a_timeset(clean_eagle, clean_tester): @@ -108,18 +107,18 @@ def test_setting_timeset_with_instance(clean_eagle, clean_tester): def test_setting_targets(clean_eagle, clean_tester): assert origen.tester.targets == [] - origen.tester.target("::DummyGenerator") - assert origen.tester.targets == ["::DummyGenerator"] + origen.tester.target("::DummyRenderer") + assert origen.tester.targets == ["::DummyRenderer"] def test_resetting_targets(): - assert origen.tester.targets == ["::DummyGenerator"] + assert origen.tester.targets == ["::DummyRenderer"] origen.tester.clear_targets() assert origen.tester.targets == [] def test_exception_on_duplicate_targets(clean_eagle, clean_tester): - origen.tester.target("::DummyGenerator") + origen.tester.target("::DummyRenderer") with pytest.raises(OSError): - origen.tester.target("::DummyGenerator") + origen.tester.target("::DummyRenderer") def test_exception_on_unknown_target(clean_eagle, clean_tester): with pytest.raises(OSError): @@ -152,24 +151,24 @@ def test_repeat(self, clean_eagle, clean_tester): assert n["attrs"][0] == "Cycle" assert n["attrs"][1][0] == 10 -def test_adding_frontend_generator(clean_eagle, clean_tester): - assert "tester_test.PyTestGenerator" not in origen.tester.generators - origen.tester.register_generator(PyTestGenerator) - assert "tester_test.PyTestGenerator" in origen.tester.generators +def test_adding_frontend_renderer(clean_eagle, clean_tester): + assert "tester_test.PyTestRenderer" not in origen.tester.testers + origen.tester.register_tester(PyTestRenderer) + assert "tester_test.PyTestRenderer" in origen.tester.testers -def test_frontend_generators_can_be_targeted(): +def test_frontend_testers_can_be_targeted(): origen.tester.clear_targets() - assert "tester_test.PyTestGenerator" in origen.tester.generators + assert "tester_test.PyTestRenderer" in origen.tester.testers assert origen.tester.targets == [] - origen.tester.target("tester_test.PyTestGenerator") - assert origen.tester.targets == ["tester_test.PyTestGenerator"] + origen.tester.target("tester_test.PyTestRenderer") + assert origen.tester.targets == ["tester_test.PyTestRenderer"] -def test_frontend_generators_can_be_targeted_as_class(): +def test_frontend_testers_can_be_targeted_as_class(): origen.tester.clear_targets() - assert "tester_test.PyTestGenerator" in origen.tester.generators + assert "tester_test.PyTestRenderer" in origen.tester.testers assert origen.tester.targets == [] - origen.tester.target(PyTestGenerator) - assert origen.tester.targets == ["tester_test.PyTestGenerator"] + origen.tester.target(PyTestRenderer) + assert origen.tester.targets == ["tester_test.PyTestRenderer"] def run_pattern(): origen.tester.cc("Pattern Start!") @@ -178,149 +177,149 @@ def run_pattern(): @pytest.fixture def tester_target_backend_dummy(): - origen.tester.target("::DummyGenerator") + origen.tester.target("::DummyRenderer") @pytest.fixture def tester_target_frontend_dummy(): try: - origen.tester.register_generator(PyTestGenerator) + origen.tester.register_tester(PyTestRenderer) except OSError: # If we get an error back that shows it's already been added, that's fine. Ignore it. pass - origen.tester.target("tester_test.PyTestGenerator") + origen.tester.target("tester_test.PyTestRenderer") -class TestBackendGenerator: - def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_backend_dummy): +class TestBackendRenderer: + def test_tester(self, capfd, clean_eagle, clean_tester, tester_target_backend_dummy): origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ "Printing StubAST to console...", - " ::DummyGenerator Node 0: Comment - Content: Pattern Start!", - " ::DummyGenerator Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGenerator Node 2: Comment - Content: Pattern End!", + " ::DummyRenderer Node 0: Comment - Content: Pattern Start!", + " ::DummyRenderer Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyRenderer Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" def test_interceptors_on_backend(self, capfd, clean_eagle, clean_tester): - origen.tester.target("::DummyGeneratorWithInterceptors") + origen.tester.target("::DummyRendererWithInterceptors") origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ - "Comment intercepted by DummyGeneratorWithInterceptors!", - "Vector intercepted by DummyGeneratorWithInterceptors!", - "Comment intercepted by DummyGeneratorWithInterceptors!", + "Comment intercepted by DummyRendererWithInterceptors!", + "Vector intercepted by DummyRendererWithInterceptors!", + "Comment intercepted by DummyRendererWithInterceptors!", "Printing StubAST to console...", - " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Pattern Start!", - " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Pattern End!", + " ::DummyRendererWithInterceptors Node 0: Comment - Content: Pattern Start!", + " ::DummyRendererWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyRendererWithInterceptors Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" -class TestFrontendGenerator: - def test_generator(self, capfd, clean_eagle, clean_tester, tester_target_frontend_dummy): +class TestFrontendRenderer: + def test_tester(self, capfd, clean_eagle, clean_tester, tester_target_frontend_dummy): origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ "Printing StubPyAST to console...", - " PyTestGenerator: Node 0: Comment - Content: Pattern Start!", - " PyTestGenerator: Node 1: Vector - Repeat: 5", - " PyTestGenerator: Node 2: Comment - Content: Pattern End!", + " PyTestRenderer: Node 0: Comment - Content: Pattern Start!", + " PyTestRenderer: Node 1: Vector - Repeat: 5", + " PyTestRenderer: Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" - def test_generator_with_interceptors(self, capfd, clean_eagle, clean_tester): - origen.tester.register_generator(PyTestGeneratorWithInterceptor) - origen.tester.target(PyTestGeneratorWithInterceptor) + def test_renderer_with_interceptors(self, capfd, clean_eagle, clean_tester): + origen.tester.register_tester(PyTestRendererWithInterceptor) + origen.tester.target(PyTestRendererWithInterceptor) origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ - "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern Start!", - "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern End!", + "Intercepted By PyTestRendererWithInterceptor: Comment - Content: Pattern Start!", + "Intercepted By PyTestRendererWithInterceptor: Comment - Content: Pattern End!", "Printing StubPyAST to console...", - " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Pattern Start!", - " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", - " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Pattern End!", + " PyTestRendererWithInterceptor: Node 0: Comment - Content: Pattern Start!", + " PyTestRendererWithInterceptor: Node 1: Vector - Repeat: 5", + " PyTestRendererWithInterceptor: Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" - def test_meta_generator(self, capfd, clean_eagle, clean_tester): - origen.tester.register_generator(PyTestMetaGenerator) - origen.tester.target(PyTestMetaGenerator) + def test_meta_renderer(self, capfd, clean_eagle, clean_tester): + origen.tester.register_tester(PyTestMetaRenderer) + origen.tester.target(PyTestMetaRenderer) origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ "Printing StubPyAST to console...", - " PyTestMetaGenerator: 0: Meta CC: Pattern Start!", - " PyTestMetaGenerator: 1: Meta Cycle: 5", - " PyTestMetaGenerator: 2: Meta CC: Pattern End!", + " PyTestMetaRenderer: 0: Meta CC: Pattern Start!", + " PyTestMetaRenderer: 1: Meta Cycle: 5", + " PyTestMetaRenderer: 2: Meta CC: Pattern End!", "" ]) assert err == "" -def test_targeted_generator_ordering(capfd, clean_eagle, clean_tester): - origen.tester.register_generator(PyTestGeneratorWithInterceptor) - origen.tester.target(PyTestGeneratorWithInterceptor) - origen.tester.target("::DummyGeneratorWithInterceptors") +def test_targeted_renderer_ordering(capfd, clean_eagle, clean_tester): + origen.tester.register_tester(PyTestRendererWithInterceptor) + origen.tester.target(PyTestRendererWithInterceptor) + origen.tester.target("::DummyRendererWithInterceptors") origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ - "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern Start!", - "Comment intercepted by DummyGeneratorWithInterceptors!", - "Vector intercepted by DummyGeneratorWithInterceptors!", - "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern End!", - "Comment intercepted by DummyGeneratorWithInterceptors!", + "Intercepted By PyTestRendererWithInterceptor: Comment - Content: Pattern Start!", + "Comment intercepted by DummyRendererWithInterceptors!", + "Vector intercepted by DummyRendererWithInterceptors!", + "Intercepted By PyTestRendererWithInterceptor: Comment - Content: Pattern End!", + "Comment intercepted by DummyRendererWithInterceptors!", "Printing StubPyAST to console...", - " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Pattern Start!", - " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", - " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Pattern End!", + " PyTestRendererWithInterceptor: Node 0: Comment - Content: Pattern Start!", + " PyTestRendererWithInterceptor: Node 1: Vector - Repeat: 5", + " PyTestRendererWithInterceptor: Node 2: Comment - Content: Pattern End!", "" "Printing StubAST to console...", - " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Pattern Start!", - " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Pattern End!", + " ::DummyRendererWithInterceptors Node 0: Comment - Content: Pattern Start!", + " ::DummyRendererWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyRendererWithInterceptors Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" -def test_targeted_generator_reverse_ordering(capfd, clean_eagle, clean_tester): - origen.tester.register_generator(PyTestGeneratorWithInterceptor) - origen.tester.target("::DummyGeneratorWithInterceptors") - origen.tester.target(PyTestGeneratorWithInterceptor) +def test_targeted_renderer_reverse_ordering(capfd, clean_eagle, clean_tester): + origen.tester.register_tester(PyTestRendererWithInterceptor) + origen.tester.target("::DummyRendererWithInterceptors") + origen.tester.target(PyTestRendererWithInterceptor) origen.tester.set_timeset("simple") run_pattern() - origen.tester.generate() + origen.tester.render() out, err = capfd.readouterr() assert out == "\n".join([ - "Comment intercepted by DummyGeneratorWithInterceptors!", - "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern Start!", - "Vector intercepted by DummyGeneratorWithInterceptors!", - "Comment intercepted by DummyGeneratorWithInterceptors!", - "Intercepted By PyTestGeneratorWithInterceptor: Comment - Content: Pattern End!", + "Comment intercepted by DummyRendererWithInterceptors!", + "Intercepted By PyTestRendererWithInterceptor: Comment - Content: Pattern Start!", + "Vector intercepted by DummyRendererWithInterceptors!", + "Comment intercepted by DummyRendererWithInterceptors!", + "Intercepted By PyTestRendererWithInterceptor: Comment - Content: Pattern End!", "Printing StubAST to console...", - " ::DummyGeneratorWithInterceptors Node 0: Comment - Content: Pattern Start!", - " ::DummyGeneratorWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", - " ::DummyGeneratorWithInterceptors Node 2: Comment - Content: Pattern End!", + " ::DummyRendererWithInterceptors Node 0: Comment - Content: Pattern Start!", + " ::DummyRendererWithInterceptors Node 1: Vector - Repeat: 5, Timeset: 'simple'", + " ::DummyRendererWithInterceptors Node 2: Comment - Content: Pattern End!", "" "Printing StubPyAST to console...", - " PyTestGeneratorWithInterceptor: Node 0: Comment - Content: Pattern Start!", - " PyTestGeneratorWithInterceptor: Node 1: Vector - Repeat: 5", - " PyTestGeneratorWithInterceptor: Node 2: Comment - Content: Pattern End!", + " PyTestRendererWithInterceptor: Node 0: Comment - Content: Pattern Start!", + " PyTestRendererWithInterceptor: Node 1: Vector - Repeat: 5", + " PyTestRendererWithInterceptor: Node 2: Comment - Content: Pattern End!", "" ]) assert err == "" diff --git a/python/origen/generator/tester_api.py b/python/origen/generator/tester_api.py index 318c2134..cbb62dc7 100644 --- a/python/origen/generator/tester_api.py +++ b/python/origen/generator/tester_api.py @@ -9,7 +9,7 @@ class TesterAPI(processor.Processor): def __init__(self): processor.Processor.__init__(self) - def generate(self): + def render(self): return self.process(origen.test_ast()) def __origen__issue_callback__(self, func, node_bytes): diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs index 2c5af285..623ae9e1 100644 --- a/rust/origen/src/core/tester.rs +++ b/rust/origen/src/core/tester.rs @@ -8,31 +8,31 @@ use crate::TEST; use crate::node; #[derive(Debug)] -pub enum Generators { +pub enum TesterSource { Internal(Box), External(String), } -impl Clone for Generators { - fn clone(&self) -> Generators { +impl Clone for TesterSource { + fn clone(&self) -> TesterSource { match self { - Generators::Internal(_g) => Generators::Internal((*_g).clone()), - Generators::External(_g) => Generators::External(_g.clone()), + TesterSource::Internal(_g) => TesterSource::Internal((*_g).clone()), + TesterSource::External(_g) => TesterSource::External(_g.clone()), } } } -impl PartialEq for Generators { - fn eq(&self, g: &Generators) -> bool { +impl PartialEq for TesterSource { + fn eq(&self, g: &TesterSource) -> bool { match g { - Generators::Internal(_g) => match self { - Generators::Internal(_self) => { + TesterSource::Internal(_g) => match self { + TesterSource::Internal(_self) => { *_g.name() == *_self.name() }, _ => false } - Generators::External(_g) => match self { - Generators::External(_self) => { + TesterSource::External(_g) => match self { + TesterSource::External(_self) => { _g == _self }, _ => false @@ -41,7 +41,7 @@ impl PartialEq for Generators { } } -impl Generators { +impl TesterSource { pub fn to_string(&self) -> String { match self { Self::External(g) => g.clone(), @@ -50,29 +50,22 @@ impl Generators { } } -#[derive(Debug)] -pub struct ExternalGenerator { - name: String, - source: String, - generator: Box, -} - #[derive(Debug)] pub struct Tester { /// The current timeset ID, if its set. /// This is the direct ID to the timeset object. /// The name and model ID can be found on this object. current_timeset_id: Option, - external_generators: IndexMap, - pub target_generators: Vec, + external_testers: IndexMap, + pub target_testers: Vec, } impl Tester { pub fn new() -> Self { Tester { current_timeset_id: Option::None, - external_generators: IndexMap::new(), - target_generators: vec!(), + external_testers: IndexMap::new(), + target_testers: vec!(), } } @@ -85,7 +78,7 @@ impl Tester { pub fn reset(&mut self) -> Result<(), Error> { self.clear_dut_dependencies()?; - self.reset_external_generators()?; + self.reset_external_testers()?; Ok(()) } @@ -96,21 +89,21 @@ impl Tester { Ok(()) } - // Resets the external generators. - // Also clears the targeted generators, as it may point to an external one that will be cleared. - pub fn reset_external_generators(&mut self) -> Result<(), Error> { - self.target_generators.clear(); - self.external_generators.clear(); + // Resets the external testers. + // Also clears the targeted testers, as it may point to an external one that will be cleared. + pub fn reset_external_testers(&mut self) -> Result<(), Error> { + self.target_testers.clear(); + self.external_testers.clear(); Ok(()) } pub fn reset_targets(&mut self) -> Result<(), Error> { - self.target_generators.clear(); + self.target_testers.clear(); Ok(()) } - pub fn register_external_generator(&mut self, generator: &str) -> Result<(), Error> { - self.external_generators.insert(generator.to_string(), Generators::External(generator.to_string())); + pub fn register_external_tester(&mut self, tester: &str) -> Result<(), Error> { + self.external_testers.insert(tester.to_string(), TesterSource::External(tester.to_string())); Ok(()) } @@ -142,11 +135,11 @@ impl Tester { } pub fn issue_callback_at(&mut self, idx: usize) -> Result<(), Error> { - let g = &mut self.target_generators[idx]; + let g = &mut self.target_testers[idx]; // Grab the last node and immutably pass it to the interceptor match g { - Generators::Internal(g_) => { + TesterSource::Internal(g_) => { let last_node = TEST.get(0).unwrap(); match &last_node.attrs { Attrs::Cycle(repeat, compressable) => g_.cycle(*repeat, *compressable, &last_node)?, @@ -173,16 +166,16 @@ impl Tester { Ok(()) } - /// Generates the output for the target at index i. - /// Allows the frontend to call generators in a loop. - pub fn generate_target_at(&mut self, idx: usize) -> Result { - let mut stat = GenerateStatus::new(); - let g = &mut self.target_generators[idx]; + /// Renders the output for the target at index i. + /// Allows the frontend to call testers in a loop. + pub fn render_target_at(&mut self, idx: usize) -> Result { + let mut stat = RenderStatus::new(); + let g = &mut self.target_testers[idx]; match g { - Generators::External(gen) => { + TesterSource::External(gen) => { stat.external.push(gen.to_string()); }, - Generators::Internal(gen) => { + TesterSource::Internal(gen) => { TEST.process(&mut |ast| gen.run(ast)); stat.completed.push(gen.to_string()) } @@ -190,50 +183,50 @@ impl Tester { Ok(stat) } - pub fn target(&mut self, generator: &str) -> Result<&Generators, Error> { + pub fn target(&mut self, tester: &str) -> Result<&TesterSource, Error> { let g; - if let Some(_g) = instantiate_tester(generator) { - g = Generators::Internal(_g); - } else if let Some(_g) = self.external_generators.get(generator) { + if let Some(_g) = instantiate_tester(tester) { + g = TesterSource::Internal(_g); + } else if let Some(_g) = self.external_testers.get(tester) { g = (*_g).clone(); } else { - return Err(Error::new(&format!("Could not find generator '{}'!", generator))); + return Err(Error::new(&format!("Could not find tester '{}'!", tester))); } - if self.target_generators.contains(&g) { - Err(Error::new(&format!("Generator {} has already been targeted!", generator))) + if self.target_testers.contains(&g) { + Err(Error::new(&format!("Tester {} has already been targeted!", tester))) } else { - self.target_generators.push(g); - Ok(&self.target_generators.last().unwrap()) + self.target_testers.push(g); + Ok(&self.target_testers.last().unwrap()) } } - pub fn targets(&self) -> &Vec { - &self.target_generators + pub fn targets(&self) -> &Vec { + &self.target_testers } pub fn targets_as_strs(&self) -> Vec { - self.target_generators.iter().map( |g| g.to_string()).collect() + self.target_testers.iter().map( |g| g.to_string()).collect() } pub fn clear_targets(&mut self) -> Result<(), Error> { - self.target_generators.clear(); + self.target_testers.clear(); Ok(()) } - pub fn generators(&self) -> Vec { + pub fn testers(&self) -> Vec { let mut gens: Vec = available_testers(); - gens.extend(self.external_generators.iter().map(|(n, _)| n.clone()).collect::>()); + gens.extend(self.external_testers.iter().map(|(n, _)| n.clone()).collect::>()); gens } } -pub struct GenerateStatus { +pub struct RenderStatus { pub completed: Vec, pub external: Vec, } -impl GenerateStatus { +impl RenderStatus { pub fn new() -> Self { Self { completed: vec!(), @@ -276,8 +269,8 @@ pub trait TesterAPI: std::fmt::Debug + crate::generator::processor::Processor + } } -impl PartialEq for dyn TesterAPI { - fn eq(&self, g: &Generators) -> bool { +impl PartialEq for dyn TesterAPI { + fn eq(&self, g: &TesterSource) -> bool { self.to_string() == g.to_string() } } diff --git a/rust/origen/src/testers/mod.rs b/rust/origen/src/testers/mod.rs index e77f2472..06c4e94f 100644 --- a/rust/origen/src/testers/mod.rs +++ b/rust/origen/src/testers/mod.rs @@ -8,8 +8,8 @@ use crate::core::tester::{TesterAPI, Interceptor}; pub fn available_testers() -> Vec { vec![ - "::DummyGenerator".to_string(), - "::DummyGeneratorWithInterceptors".to_string(), + "::DummyRenderer".to_string(), + "::DummyRendererWithInterceptors".to_string(), "::V93K::ST7".to_string(), "::Simulator".to_string(), ] @@ -17,21 +17,21 @@ pub fn available_testers() -> Vec { pub fn instantiate_tester(g: &str) -> Option> { match &g { - &"::DummyGenerator" => Some(Box::new(DummyGenerator::default())), - &"::DummyGeneratorWithInterceptors" => Some(Box::new(DummyGeneratorWithInterceptors::default())), - &"::V93K::ST7" => Some(Box::new(v93k::Generator::default())), - &"::Simulator" => Some(Box::new(simulator::Generator::default())), + &"::DummyRenderer" => Some(Box::new(DummyRenderer::default())), + &"::DummyRendererWithInterceptors" => Some(Box::new(DummyRendererWithInterceptors::default())), + &"::V93K::ST7" => Some(Box::new(v93k::Renderer::default())), + &"::Simulator" => Some(Box::new(simulator::Renderer::default())), _ => None } } #[derive(Debug, Clone)] -pub struct DummyGenerator { +pub struct DummyRenderer { count: usize, current_timeset_id: Option, } -impl Default for DummyGenerator { +impl Default for DummyRenderer { fn default() -> Self { Self { count: 0, @@ -40,11 +40,11 @@ impl Default for DummyGenerator { } } -impl DummyGenerator {} -impl Interceptor for DummyGenerator {} -impl TesterAPI for DummyGenerator { +impl DummyRenderer {} +impl Interceptor for DummyRenderer {} +impl TesterAPI for DummyRenderer { fn name(&self) -> String { - "DummyGenerator".to_string() + "DummyRenderer".to_string() } fn clone(&self) -> Box { @@ -56,7 +56,7 @@ impl TesterAPI for DummyGenerator { } } -impl Processor for DummyGenerator { +impl Processor for DummyRenderer { fn on_test(&mut self, _name: &str, _node: &Node) -> Return { // Not counting the top node as a node. Only comments and cycles. println!("Printing StubAST to console..."); @@ -64,7 +64,7 @@ impl Processor for DummyGenerator { } fn on_comment(&mut self, _level: u8, msg: &str, _node: &Node) -> Return { - println!(" ::DummyGenerator Node {}: Comment - Content: {}", self.count, msg); + println!(" ::DummyRenderer Node {}: Comment - Content: {}", self.count, msg); self.count += 1; Return::Unmodified } @@ -72,7 +72,7 @@ impl Processor for DummyGenerator { fn on_cycle(&mut self, repeat: u32, _compressable: bool, _node: &Node) -> Return { let dut = dut(); let t = &dut.timesets[self.current_timeset_id.unwrap()]; - println!(" ::DummyGenerator Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); + println!(" ::DummyRenderer Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); self.count += 1; Return::Unmodified } @@ -84,16 +84,16 @@ impl Processor for DummyGenerator { } #[derive(Debug, Clone)] -pub struct DummyGeneratorWithInterceptors { +pub struct DummyRendererWithInterceptors { count: usize, current_timeset_id: Option, } -impl DummyGeneratorWithInterceptors {} +impl DummyRendererWithInterceptors {} -impl TesterAPI for DummyGeneratorWithInterceptors { +impl TesterAPI for DummyRendererWithInterceptors { fn name(&self) -> String { - "DummyGeneratorWithInterceptors".to_string() + "DummyRendererWithInterceptors".to_string() } fn clone(&self) -> Box { @@ -107,7 +107,7 @@ impl TesterAPI for DummyGeneratorWithInterceptors { } } -impl Default for DummyGeneratorWithInterceptors { +impl Default for DummyRendererWithInterceptors { fn default() -> Self { Self { count: 0, @@ -116,19 +116,19 @@ impl Default for DummyGeneratorWithInterceptors { } } -impl Interceptor for DummyGeneratorWithInterceptors { +impl Interceptor for DummyRendererWithInterceptors { fn cycle(&mut self, _repeat: u32, _compressable: bool, _node: &Node) -> Result<(), Error> { - println!("Vector intercepted by DummyGeneratorWithInterceptors!"); + println!("Vector intercepted by DummyRendererWithInterceptors!"); Ok(()) } fn cc(&mut self, _level: u8, _msg: &str, _node: &Node) -> Result<(), Error> { - println!("Comment intercepted by DummyGeneratorWithInterceptors!"); + println!("Comment intercepted by DummyRendererWithInterceptors!"); Ok(()) } } -impl Processor for DummyGeneratorWithInterceptors { +impl Processor for DummyRendererWithInterceptors { fn on_test(&mut self, _name: &str, _node: &Node) -> Return { // Not counting the top node as a node. Only comments and cycles. @@ -137,7 +137,7 @@ impl Processor for DummyGeneratorWithInterceptors { } fn on_comment(&mut self, _level: u8, msg: &str, _node: &Node) -> Return { - println!(" ::DummyGeneratorWithInterceptors Node {}: Comment - Content: {}", self.count, msg); + println!(" ::DummyRendererWithInterceptors Node {}: Comment - Content: {}", self.count, msg); self.count += 1; Return::Unmodified } @@ -145,7 +145,7 @@ impl Processor for DummyGeneratorWithInterceptors { fn on_cycle(&mut self, repeat: u32, _compressable: bool, _node: &Node) -> Return { let dut = dut(); let t = &dut.timesets[self.current_timeset_id.unwrap()]; - println!(" ::DummyGeneratorWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); + println!(" ::DummyRendererWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); self.count += 1; Return::Unmodified } diff --git a/rust/origen/src/testers/simulator/mod.rs b/rust/origen/src/testers/simulator/mod.rs index c24fd03f..e9055ab4 100644 --- a/rust/origen/src/testers/simulator/mod.rs +++ b/rust/origen/src/testers/simulator/mod.rs @@ -4,16 +4,16 @@ use crate::generator::ast::{Node}; use crate::core::tester::{TesterAPI, Interceptor}; #[derive(Debug, Clone)] -pub struct Generator {} +pub struct Renderer {} -impl Default for Generator { +impl Default for Renderer { fn default() -> Self { Self {} } } -impl Processor for Generator {} -impl Interceptor for Generator { +impl Processor for Renderer {} +impl Interceptor for Renderer { fn clear_timeset(&mut self, _node: &Node) -> Result<(), Error> { println!(""); Ok(()) @@ -35,9 +35,9 @@ impl Interceptor for Generator { } } -impl TesterAPI for Generator { +impl TesterAPI for Renderer { fn name(&self) -> String { - "DummyGenerator".to_string() + "Simulator".to_string() } fn clone(&self) -> Box { diff --git a/rust/origen/src/testers/v93k/mod.rs b/rust/origen/src/testers/v93k/mod.rs index 1b2b5e5d..5957d781 100644 --- a/rust/origen/src/testers/v93k/mod.rs +++ b/rust/origen/src/testers/v93k/mod.rs @@ -4,11 +4,11 @@ use crate::generator::ast::{Node}; use crate::core::tester::{TesterAPI, Interceptor}; #[derive(Debug, Clone)] -pub struct Generator { +pub struct Renderer { current_timeset_id: Option } -impl Default for Generator { +impl Default for Renderer { fn default() -> Self { Self { current_timeset_id: None @@ -16,10 +16,10 @@ impl Default for Generator { } } -impl Interceptor for Generator {} -impl TesterAPI for Generator { +impl Interceptor for Renderer {} +impl TesterAPI for Renderer { fn name(&self) -> String { - "DummyGenerator".to_string() + "V93K_ST7".to_string() } fn clone(&self) -> Box { @@ -31,7 +31,7 @@ impl TesterAPI for Generator { } } -impl Processor for Generator { +impl Processor for Renderer { fn on_comment(&mut self, _level: u8, msg: &str, _node: &Node) -> Return { println!("# {}", msg); Return::Unmodified diff --git a/rust/pyapi/src/tester.rs b/rust/pyapi/src/tester.rs index 7edc3296..ee19c24a 100644 --- a/rust/pyapi/src/tester.rs +++ b/rust/pyapi/src/tester.rs @@ -3,7 +3,7 @@ use pyo3::types::{PyAny, PyDict, PyTuple}; use super::timesets::timeset::{Timeset}; use std::collections::HashMap; use origen::error::Error; -use origen::core::tester::{Generators}; +use origen::core::tester::{TesterSource}; use origen::TEST; #[pymodule] @@ -15,8 +15,8 @@ pub fn tester(_py: Python, m: &PyModule) -> PyResult<()> { #[pyclass(subclass)] #[derive(Debug)] pub struct PyTester { - python_generators: HashMap, - instantiated_generators: HashMap, + python_testers: HashMap, + instantiated_testers: HashMap, metadata: Vec, } @@ -26,8 +26,8 @@ impl PyTester { fn new(obj: &PyRawObject) { origen::tester().reset().unwrap(); obj.init({ PyTester { - python_generators: HashMap::new(), - instantiated_generators: HashMap::new(), + python_testers: HashMap::new(), + instantiated_testers: HashMap::new(), metadata: vec!(), } }); @@ -49,8 +49,8 @@ impl PyTester { Ok(slf.to_object(py)) } - fn reset_external_generators(slf: PyRef) -> PyResult { - origen::tester().reset_external_generators()?; + fn reset_external_testers(slf: PyRef) -> PyResult { + origen::tester().reset_external_testers()?; let gil = Python::acquire_gil(); let py = gil.python(); @@ -138,7 +138,7 @@ impl PyTester { } fn issue_callbacks(&self, func: &str) -> PyResult<()> { - // Get the current targeted generators + // Get the current targeted testers let targets; { let tester = origen::tester(); @@ -148,12 +148,12 @@ impl PyTester { // issue callbacks in the order which they were targeted for (i, t) in targets.iter().enumerate() { match t { - Generators::External(g) => { - // External generators which the backend can't generate itself. Need to generate them here. - match self.instantiated_generators.get(g) { + TesterSource::External(g) => { + // External testers which the backend can't render itself. Need to render them here. + match self.instantiated_testers.get(g) { Some(inst) => { - // The generator here is a PyObject - a handle on the class itself. - // Instantiate it and call its generate method with the AST. + // The tester here is a PyObject - a handle on the class itself. + // Instantiate it and call its render method with the AST. let gil = Python::acquire_gil(); let py = gil.python(); let last_node = TEST.get(0).unwrap().to_pickle(); @@ -164,7 +164,7 @@ impl PyTester { // whether it happens here or there. inst.call_method1(py, "__origen__issue_callback__", args)?; }, - None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), + None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python tester {} cannot be found!", g)))), } }, _ => { @@ -204,7 +204,7 @@ impl PyTester { Self::cycle(slf, Some(&kwargs)) } - fn register_generator(&mut self, g: &PyAny) -> PyResult<()> { + fn register_tester(&mut self, g: &PyAny) -> PyResult<()> { let mut tester = origen::tester(); let gil = Python::acquire_gil(); let py = gil.python(); @@ -213,17 +213,17 @@ impl PyTester { let mut n = obj.getattr(py, "__module__")?.extract::(py)?; n.push_str(&format!(".{}", obj.getattr(py, "__qualname__")?.extract::(py)?)); - tester.register_external_generator(&n)?; - self.python_generators.insert(n, obj); + tester.register_external_tester(&n)?; + self.python_testers.insert(n, obj); Ok(()) } - #[args(generators="*")] - fn target(&mut self, generators: &PyTuple) -> PyResult> { - if generators.len() > 0 { + #[args(testers="*")] + fn target(&mut self, testers: &PyTuple) -> PyResult> { + if testers.len() > 0 { let mut tester = origen::tester(); - for g in generators.iter() { - // Accept either a string name or the actual class of the generator + for g in testers.iter() { + // Accept either a string name or the actual class of the tester if let Ok(name) = g.extract::() { tester.target(&name)?; } else { @@ -235,10 +235,10 @@ impl PyTester { n.push_str(&format!(".{}", obj.getattr(py, "__qualname__")?.extract::(py)?)); let t = tester.target(&n)?; match t { - Generators::External(gen) => { - let klass = self.python_generators.get(gen).unwrap(); + TesterSource::External(gen) => { + let klass = self.python_testers.get(gen).unwrap(); let inst = klass.call0(py)?; - self.instantiated_generators.insert(gen.to_string(), inst); + self.instantiated_testers.insert(gen.to_string(), inst); }, _ => {} } @@ -263,7 +263,7 @@ impl PyTester { Ok(slf.to_object(py)) } - fn generate(&self) -> PyResult<()> { + fn render(&self) -> PyResult<()> { let targets; { let tester = origen::tester(); @@ -271,23 +271,23 @@ impl PyTester { } for (i, t) in targets.iter().enumerate() { match t { - Generators::External(g) => { - // External generators which the backend can't generate itself. Need to generate them here. - match self.instantiated_generators.get(g) { + TesterSource::External(g) => { + // External testers which the backend can't render itself. Need to render them here. + match self.instantiated_testers.get(g) { Some(inst) => { - // The generator here is a PyObject - a handle on the class itself. - // Instantiate it and call its generate method with the AST. + // The tester here is a PyObject - a handle on the class itself. + // Instantiate it and call its render method with the AST. let gil = Python::acquire_gil(); let py = gil.python(); - inst.call_method0(py, "generate")?; + inst.call_method0(py, "render")?; }, - None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python generator {} cannot be found!", g)))), + None => return Err(PyErr::from(Error::new(&format!("Something's gone wrong and Python tester {} cannot be found!", g)))), } }, _ => { let mut tester = origen::tester(); //let dut = origen::DUT.lock().unwrap(); - tester.generate_target_at(i)?; + tester.render_target_at(i)?; } } } @@ -295,8 +295,8 @@ impl PyTester { } #[getter] - fn generators(&self) -> PyResult> { + fn testers(&self) -> PyResult> { let tester = origen::tester(); - Ok(tester.generators()) + Ok(tester.testers()) } } From 559904076106e43ed614584d0e456948b4ce6be7 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Sat, 21 Mar 2020 08:16:04 -0500 Subject: [PATCH 14/18] Revert dut(), tester(), and services() to wait on mutex lock --- .../src/core/model/registers/bit_collection.rs | 8 ++++---- rust/origen/src/lib.rs | 12 ------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/rust/origen/src/core/model/registers/bit_collection.rs b/rust/origen/src/core/model/registers/bit_collection.rs index 4c6986f1..62ebeb91 100644 --- a/rust/origen/src/core/model/registers/bit_collection.rs +++ b/rust/origen/src/core/model/registers/bit_collection.rs @@ -701,7 +701,7 @@ impl<'a> BitCollection<'a> { #[cfg(test)] mod tests { use crate::core::model::registers::{Bit, BitCollection}; - use crate::{dut_or_wait, Dut}; + use crate::{dut, Dut}; use num_bigint::ToBigUint; use std::sync::MutexGuard; @@ -730,7 +730,7 @@ mod tests { #[test] fn data_method_works() { - let mut dut = dut_or_wait(); + let mut dut = dut(); let bc = make_bit_collection(16, &mut dut); assert_eq!(bc.data().unwrap(), 0.to_biguint().unwrap()); @@ -738,7 +738,7 @@ mod tests { #[test] fn set_data_method_works() { - let mut dut = dut_or_wait(); + let mut dut = dut(); let bc = make_bit_collection(16, &mut dut); bc.set_data(0.to_biguint().unwrap()); @@ -751,7 +751,7 @@ mod tests { #[test] fn range_method_works() { - let mut dut = dut_or_wait(); + let mut dut = dut(); let bc = make_bit_collection(16, &mut dut); bc.set_data(0x1234.to_biguint().unwrap()); diff --git a/rust/origen/src/lib.rs b/rust/origen/src/lib.rs index 6a061885..c7445594 100644 --- a/rust/origen/src/lib.rs +++ b/rust/origen/src/lib.rs @@ -81,26 +81,14 @@ pub enum Value<'a> { } pub fn dut() -> MutexGuard<'static, Dut> { - DUT.try_lock().expect("Backend Error: Unable to acquire DUT lock!") -} - -pub fn dut_or_wait() -> MutexGuard<'static, Dut> { DUT.lock().unwrap() } pub fn tester() -> MutexGuard<'static, Tester> { - TESTER.try_lock().expect("Backend Error: Unable to acquire TESTER lock!") -} - -pub fn tester_or_wait() -> MutexGuard<'static, Tester> { TESTER.lock().unwrap() } pub fn services() -> MutexGuard<'static, Services> { - SERVICES.try_lock().expect("Backend Error: Unable to acquire SERVICESs lock!") -} - -pub fn services_or_wait() -> MutexGuard<'static, Services> { SERVICES.lock().unwrap() } From e94ec26e09ec91f7b21ebc8a147a48e01a485de8 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Sat, 21 Mar 2020 15:06:23 -0500 Subject: [PATCH 15/18] began work on #88 - target system updates --- rust/origen/cli/src/bin.rs | 115 ++++++++++++++++++------- rust/origen/cli/src/commands/target.rs | 34 ++++---- 2 files changed, 103 insertions(+), 46 deletions(-) diff --git a/rust/origen/cli/src/bin.rs b/rust/origen/cli/src/bin.rs index d84aea99..17bcd01c 100644 --- a/rust/origen/cli/src/bin.rs +++ b/rust/origen/cli/src/bin.rs @@ -124,26 +124,69 @@ fn main() { .subcommand(SubCommand::with_name("target") .about("Set/view the default target") .visible_alias("t") - .arg(Arg::with_name("targets") - .help("The name of the file(s) from targets/ to be set, added, or removed to the default target(s)") - .takes_value(true) - .value_name("TARGETS") - .multiple(true) + // .arg(Arg::with_name("targets") + // .help("The name of the file(s) from targets/ to be set, added, or removed to the default target(s)") + // ) + .subcommand(SubCommand::with_name("add") + .about("Activates the given target(s)") + .visible_alias("a") + .arg(Arg::with_name("targets") + .help("Targets to be activated") + .takes_value(true) + .value_name("TARGETS") + .multiple(true) + .required(true) + ) + ) + .subcommand(SubCommand::with_name("remove") + .about("Deactivates the given target(s)") + .visible_alias("r") + .arg(Arg::with_name("targets") + .help("Targets to be deactivated") + .takes_value(true) + .value_name("TARGETS") + .multiple(true) + .required(true) + ) ) - .arg(Arg::with_name("add") - .help("Adds the given target(s) to the current default target(s)") - .short("a") - .long("add") - .takes_value(false) - .conflicts_with("remove") + .subcommand(SubCommand::with_name("set") + .about("Activates the given target(s) while deactivating all others") + .visible_alias("s") + .arg(Arg::with_name("targets") + .help("Targets to be set") + .takes_value(true) + .value_name("TARGETS") + .multiple(true) + .required(true) + ) ) - .arg(Arg::with_name("remove") - .help("Removes the given target(s) to the current default target(s)") - .short("r") - .long("remove") - .takes_value(false) - .conflicts_with("add") + .subcommand(SubCommand::with_name("default") + .about("Activates the default target(s) while deactivating all others") + .visible_alias("d") ) + .subcommand(SubCommand::with_name("view") + .about("Views the currently activated target(s)") + .visible_alias("v") + ) + + // .arg(Arg::with_name("add") + // .help("Adds the given target(s) to the current default target(s)") + // .short("a") + // .long("add") + // .takes_value(false) + // .conflicts_with("remove") + // ) + // .arg(Arg::with_name("remove") + // .help("Removes the given target(s) to the current default target(s)") + // .short("r") + // .long("remove") + // .takes_value(false) + // .conflicts_with("add") + // ) + // .arg(Arg::with_name("clear") + // .help("Clears all the current targets") + // .short("") + // ) ) /************************************************************************************/ @@ -208,21 +251,31 @@ fn main() { ); } Some("target") => { + // let m = matches.subcommand_matches("target").unwrap(); + // let a; + // if m.value_of("add").is_some() { + // a = Some("add"); + // } else if m.value_of("remove").is_some() { + // a = Some("remove"); + // } else { + // a = None; + // } + // //commands::target::run(matches.value_of("target")); + // if let Some(targets) = m.values_of("targets") { + // commands::target::run(Some(targets.collect()), a); + // } else { + // commands::target::run(None, a); + // } let m = matches.subcommand_matches("target").unwrap(); - let a; - if m.value_of("add").is_some() { - a = Some("add"); - } else if m.value_of("remove").is_some() { - a = Some("remove"); - } else { - a = None; - } - //commands::target::run(matches.value_of("target")); - if let Some(targets) = m.values_of("targets") { - commands::target::run(Some(targets.collect()), a); - } else { - commands::target::run(None, a); - } + commands::target::run( + m.subcommand_name(), + { + match m.values_of("targets") { + Some(targets) => Some(targets.collect()), + None => None, + } + } + ); } Some("mode") => { let matches = matches.subcommand_matches("mode").unwrap(); diff --git a/rust/origen/cli/src/commands/target.rs b/rust/origen/cli/src/commands/target.rs index bc96fc33..cdc7ba60 100644 --- a/rust/origen/cli/src/commands/target.rs +++ b/rust/origen/cli/src/commands/target.rs @@ -1,23 +1,27 @@ use origen::core::application::target; use origen::APPLICATION_CONFIG; -pub fn run(tnames: Option>, action: Option<&str>) { - if tnames.is_none() { - if APPLICATION_CONFIG.target.is_some() { - let name = APPLICATION_CONFIG.target.clone().unwrap(); - println!("{}", name); - } else { - println!("No default target is currently enabled in this workspace"); - } +pub fn run(subcmd: Option<&str>, tnames: Option>) { + if subcmd.is_none() { + run(Some("view"), None) } else { - for name in tnames.unwrap().iter() { - //let name = tname.unwrap(); - if name == &"default" { - target::delete_val("target"); + if tnames.is_none() { + if APPLICATION_CONFIG.target.is_some() { + let name = APPLICATION_CONFIG.target.clone().unwrap(); + println!("{}", name); } else { - let c = target::clean_name(name, "targets", false); - target::set_workspace("target", &c); - println!("Your workspace target is now set to: {}", c); + println!("No default target is currently enabled in this workspace"); + } + } else { + for name in tnames.unwrap().iter() { + //let name = tname.unwrap(); + if name == &"default" { + target::delete_val("target"); + } else { + let c = target::clean_name(name, "targets", false); + target::set_workspace("target", &c); + println!("Your workspace target is now set to: {}", c); + } } } } From d45ecb1f079ad4fcd5b2683a40a7fe95f6a0e18c Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Tue, 31 Mar 2020 22:53:00 -0500 Subject: [PATCH 16/18] Merge with latest AST changes --- rust/origen/cli/src/commands/target.rs | 2 +- rust/origen/src/core/tester.rs | 4 +- rust/origen/src/generator/ast.rs | 63 ------------------------ rust/origen/src/generator/mod.rs | 6 +-- rust/origen/src/generator/nodes.rs | 2 + rust/origen/src/testers/mod.rs | 32 ++++++------ rust/origen/src/testers/simulator/mod.rs | 4 +- rust/origen/src/testers/v93k/mod.rs | 14 +++--- 8 files changed, 31 insertions(+), 96 deletions(-) diff --git a/rust/origen/cli/src/commands/target.rs b/rust/origen/cli/src/commands/target.rs index bc96fc33..3fa58d4d 100644 --- a/rust/origen/cli/src/commands/target.rs +++ b/rust/origen/cli/src/commands/target.rs @@ -1,7 +1,7 @@ use origen::core::application::target; use origen::APPLICATION_CONFIG; -pub fn run(tnames: Option>, action: Option<&str>) { +pub fn run(tnames: Option>, _action: Option<&str>) { if tnames.is_none() { if APPLICATION_CONFIG.target.is_some() { let name = APPLICATION_CONFIG.target.clone().unwrap(); diff --git a/rust/origen/src/core/tester.rs b/rust/origen/src/core/tester.rs index 623ae9e1..e3135c68 100644 --- a/rust/origen/src/core/tester.rs +++ b/rust/origen/src/core/tester.rs @@ -176,7 +176,7 @@ impl Tester { stat.external.push(gen.to_string()); }, TesterSource::Internal(gen) => { - TEST.process(&mut |ast| gen.run(ast)); + TEST.process(&mut |ast| gen.run(ast).unwrap()); stat.completed.push(gen.to_string()) } } @@ -262,7 +262,7 @@ impl<'a, T> Interceptor for &'a mut T where T: TesterAPI {} pub trait TesterAPI: std::fmt::Debug + crate::generator::processor::Processor + Interceptor { fn name(&self) -> String; fn clone(&self) -> Box; - fn run(&mut self, node: &Node) -> Node; + fn run(&mut self, node: &Node) -> crate::Result; fn to_string(&self) -> String { format!("::{}", self.name()) diff --git a/rust/origen/src/generator/ast.rs b/rust/origen/src/generator/ast.rs index d2162323..45a150f5 100644 --- a/rust/origen/src/generator/ast.rs +++ b/rust/origen/src/generator/ast.rs @@ -19,69 +19,6 @@ macro_rules! node { }; } -#[derive(Clone, Debug, PartialEq, Serialize)] -pub enum Attrs { - // A meta-node type, used to indicate a node who's children should be placed inline at the given location - _Inline, - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - //// Test (pat gen) nodes - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - Test(String), - Comment(u8, String), // level, msg - SetTimeset(usize), // Indicates both a set or change of the current timeset - ClearTimeset(), - PinWrite(Id, u128), - PinVerify(Id, u128), - RegWrite(Id, BigUint, Option, Option), // reg_id, data, overlay_enable, overlay_str - RegVerify( - Id, - BigUint, - Option, - Option, - Option, - Option, - ), // reg_id, data, verify_enable, capture_enable, overlay_enable, overlay_str - JTAGWriteIR(u32, BigUint, Option, Option), // size, data, overlay_enable, overlay_str - JTAGVerifyIR( - u32, - BigUint, - Option, - Option, - Option, - Option, - ), // size, data, verify_enable, capture_enable, overlay_enable, overlay_str - JTAGWriteDR(u32, BigUint, Option, Option), // size, data, overlay_enable, overlay_str - JTAGVerifyDR( - u32, - BigUint, - Option, - Option, - Option, - Option, - ), // size, data, verify_enable, capture_enable, overlay_enable, overlay_str - Cycle(u32, bool), // repeat (0 not allowed), compressable - - //// Teradyne custom nodes - - //// Advantest custom nodes - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - //// Flow (prog gen) nodes - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - Flow(String), -} - -impl Node { - /// Returns a new node which is the output of the node processed by the given processor. - /// Returning None means that the processor has decided that the node should be removed - /// from the next stage AST. - pub fn process(&self, processor: &mut dyn Processor) -> Option { - let r = processor.on_node(&self); - self.process_return_code(r, processor) - } -} - /// An AST provides an API for constructing a node tree, when completed it can be unwrapped /// to a node by calling the unwrap() method #[derive(Clone)] diff --git a/rust/origen/src/generator/mod.rs b/rust/origen/src/generator/mod.rs index 7d725a98..110082f7 100644 --- a/rust/origen/src/generator/mod.rs +++ b/rust/origen/src/generator/mod.rs @@ -84,11 +84,7 @@ mod tests { // Test upcase comments processor -<<<<<<< HEAD - let new_ast = test.process(&mut |ast| UpcaseComments::run(ast)); -======= - let new_ast = test.process(&|ast| UpcaseComments::run(ast).expect("comments upcased")); ->>>>>>> origin/master + let new_ast = test.process(&mut |ast| UpcaseComments::run(ast).expect("comments upcased")); let mut ast = AST::new(); ast.push_and_open(node!(Test, "trim_vbgap".to_string())); diff --git a/rust/origen/src/generator/nodes.rs b/rust/origen/src/generator/nodes.rs index f2c0fef0..9e9b1b76 100644 --- a/rust/origen/src/generator/nodes.rs +++ b/rust/origen/src/generator/nodes.rs @@ -18,6 +18,8 @@ pub enum Attrs { ////////////////////////////////////////////////////////////////////////////////////////////////////////// Test(String), Comment(u8, String), // level, msg + SetTimeset(usize), // Indicates both a set or change of the current timeset + ClearTimeset(), PinWrite(Id, u128), PinVerify(Id, u128), RegWrite(Id, BigUint, Option, Option), // reg_id, data, overlay_enable, overlay_str diff --git a/rust/origen/src/testers/mod.rs b/rust/origen/src/testers/mod.rs index 5fb2dd32..460d6fc3 100644 --- a/rust/origen/src/testers/mod.rs +++ b/rust/origen/src/testers/mod.rs @@ -51,36 +51,36 @@ impl TesterAPI for DummyRenderer { Box::new(std::clone::Clone::clone(self)) } - fn run(&mut self, node: &Node) -> Node { - node.process(self).unwrap() + fn run(&mut self, node: &Node) -> crate::Result { + Ok(node.process(self)?.unwrap()) } } impl Processor for DummyRenderer { - fn on_node(&mut self, node: &Node) -> Return { + fn on_node(&mut self, node: &Node) -> crate::Result { match &node.attrs { Attrs::Test(_name) => { // Not counting the top node as a node. Only comments and cycles. println!("Printing StubAST to console..."); - Return::ProcessChildren + Ok(Return::ProcessChildren) } Attrs::Comment(_level, msg) => { println!(" ::DummyRenderer Node {}: Comment - Content: {}", self.count, msg); self.count += 1; - Return::Unmodified + Ok(Return::Unmodified) }, Attrs::Cycle(repeat, _compressable) => { let dut = dut(); let t = &dut.timesets[self.current_timeset_id.unwrap()]; println!(" ::DummyRenderer Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); self.count += 1; - Return::Unmodified + Ok(Return::Unmodified) }, Attrs::SetTimeset(timeset_id) => { self.current_timeset_id = Some(*timeset_id); - Return::Unmodified + Ok(Return::Unmodified) }, - _ => Return::ProcessChildren, + _ => Ok(Return::ProcessChildren), } } } @@ -102,9 +102,9 @@ impl TesterAPI for DummyRendererWithInterceptors { Box::new(std::clone::Clone::clone(self)) } - fn run(&mut self, node: &Node) -> Node { + fn run(&mut self, node: &Node) -> crate::Result { //let mut slf = Self::default(); - node.process(self).unwrap() + Ok(node.process(self)?.unwrap()) //node.clone() } } @@ -131,30 +131,30 @@ impl Interceptor for DummyRendererWithInterceptors { } impl Processor for DummyRendererWithInterceptors { - fn on_node(&mut self, node: &Node) -> Return { + fn on_node(&mut self, node: &Node) -> crate::Result { match &node.attrs { Attrs::Test(_name) => { // Not counting the top node as a node. Only comments and cycles. println!("Printing StubAST to console..."); - Return::ProcessChildren + Ok(Return::ProcessChildren) } Attrs::Comment(_level, msg) => { println!(" ::DummyRendererWithInterceptors Node {}: Comment - Content: {}", self.count, msg); self.count += 1; - Return::Unmodified + Ok(Return::Unmodified) }, Attrs::Cycle(repeat, _compressable) => { let dut = dut(); let t = &dut.timesets[self.current_timeset_id.unwrap()]; println!(" ::DummyRendererWithInterceptors Node {}: Vector - Repeat: {}, Timeset: '{}'", self.count, repeat, t.name); self.count += 1; - Return::Unmodified + Ok(Return::Unmodified) }, Attrs::SetTimeset(timeset_id) => { self.current_timeset_id = Some(*timeset_id); - Return::Unmodified + Ok(Return::Unmodified) }, - _ => Return::ProcessChildren, + _ => Ok(Return::ProcessChildren), } } } diff --git a/rust/origen/src/testers/simulator/mod.rs b/rust/origen/src/testers/simulator/mod.rs index e9055ab4..eeddec2a 100644 --- a/rust/origen/src/testers/simulator/mod.rs +++ b/rust/origen/src/testers/simulator/mod.rs @@ -44,7 +44,7 @@ impl TesterAPI for Renderer { Box::new(std::clone::Clone::clone(self)) } - fn run(&mut self, node: &Node) -> Node { - node.process(self).unwrap() + fn run(&mut self, node: &Node) -> crate::Result { + Ok(node.process(self)?.unwrap()) } } diff --git a/rust/origen/src/testers/v93k/mod.rs b/rust/origen/src/testers/v93k/mod.rs index 64e2413f..dcb211dd 100644 --- a/rust/origen/src/testers/v93k/mod.rs +++ b/rust/origen/src/testers/v93k/mod.rs @@ -26,29 +26,29 @@ impl TesterAPI for Renderer { Box::new(std::clone::Clone::clone(self)) } - fn run(&mut self, node: &Node) -> Node { - node.process(self).unwrap() + fn run(&mut self, node: &Node) -> crate::Result { + Ok(node.process(self)?.unwrap()) } } impl Processor for Renderer { - fn on_node(&mut self, node: &Node) -> Return { + fn on_node(&mut self, node: &Node) -> crate::Result { match &node.attrs { Attrs::Comment(_level, msg) => { println!("# {}", msg); - Return::Unmodified + Ok(Return::Unmodified) }, Attrs::Cycle(repeat, _compressable) => { let dut = DUT.lock().unwrap(); let t = &dut.timesets[self.current_timeset_id.unwrap()]; println!("R{} {} # ;", repeat, t.name); - Return::Unmodified + Ok(Return::Unmodified) }, Attrs::SetTimeset(timeset_id) => { self.current_timeset_id = Some(*timeset_id); - Return::Unmodified + Ok(Return::Unmodified) }, - _ => Return::ProcessChildren, + _ => Ok(Return::ProcessChildren), } } } From d986b65a6abe4119c4aaa30c0defedafb4ea929f Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Tue, 31 Mar 2020 22:54:03 -0500 Subject: [PATCH 17/18] Merge with latest AST changes --- rust/pyapi/Cargo.lock | 323 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) diff --git a/rust/pyapi/Cargo.lock b/rust/pyapi/Cargo.lock index 9232d8e8..9fdf8c92 100644 --- a/rust/pyapi/Cargo.lock +++ b/rust/pyapi/Cargo.lock @@ -9,6 +9,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "autocfg" version = "0.1.7" @@ -21,12 +33,72 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "backtrace" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" +dependencies = [ + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + [[package]] name = "built" version = "0.3.2" @@ -39,6 +111,12 @@ dependencies = [ "toml 0.5.5", ] +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -87,6 +165,23 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg 1.0.0", + "cfg-if", + "lazy_static 1.4.0", +] + [[package]] name = "ctor" version = "0.1.12" @@ -97,12 +192,67 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi", +] + [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "enum-utils" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed327f716d0d351d86c9fd3398d20ee39ad8f681873cc081da2ca1c10fed398a" +dependencies = [ + "enum-utils-from-str", + "failure", + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "enum-utils-from-str" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49be08bad6e4ca87b2b8e74146987d4e5cb3b7512efa50ef505b51a22227ee1" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "eval" version = "0.4.3" @@ -114,6 +264,41 @@ dependencies = [ "serde_json", ] +[[package]] +name = "failure" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" +dependencies = [ + "backtrace", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "ghost" version = "0.1.1" @@ -300,6 +485,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.8" @@ -417,12 +608,19 @@ dependencies = [ "autocfg 1.0.0", ] +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "origen" version = "2.0.0-pre0" dependencies = [ "built", "config", + "enum-utils", "eval", "indexmap", "lazy_static 1.4.0", @@ -432,11 +630,14 @@ dependencies = [ "num-traits 0.2.11", "path-clean", "pathdiff", + "pest", + "pest_derive", "pyo3", "regex", "semver", "serde 1.0.104", "serde-pickle", + "shellexpand", "termcolor", "time", "walkdir", @@ -482,6 +683,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pkg-config" version = "0.3.17" @@ -586,6 +830,17 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.3.3" @@ -604,12 +859,30 @@ version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rust-ini" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + [[package]] name = "ryu" version = "1.0.2" @@ -692,6 +965,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_derive_internals" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_json" version = "1.0.44" @@ -712,6 +996,27 @@ dependencies = [ "serde 0.8.23", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "shellexpand" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7e79eddc7b411f9beeaaf2d421de7e7cb3b1ab9eaf1b79704c0e4130cba6b5" +dependencies = [ + "dirs", +] + [[package]] name = "smallvec" version = "1.1.0" @@ -782,6 +1087,18 @@ dependencies = [ "serde 1.0.104", ] +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-bidi" version = "0.3.4" @@ -852,6 +1169,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "winapi" version = "0.3.8" From fe0a94cce6293852965448066cf97f345cecd3e7 Mon Sep 17 00:00:00 2001 From: Corey Engelken Date: Sat, 4 Apr 2020 22:52:17 -0500 Subject: [PATCH 18/18] ~90% of issue #88 --- example/config/application.toml | 2 +- rust/origen/cli/src/bin.rs | 54 +++---------- rust/origen/cli/src/commands/mode.rs | 4 +- rust/origen/cli/src/commands/target.rs | 56 ++++++------- rust/origen/src/core/application/config.rs | 18 +++-- rust/origen/src/core/application/target.rs | 92 +++++++++++++++++++++- rust/origen/src/core/mod.rs | 2 + rust/origen/src/core/utility/logger.rs | 29 +++++++ rust/origen/src/core/utility/mod.rs | 2 + rust/origen/src/lib.rs | 6 +- rust/pyapi/src/lib.rs | 11 +-- 11 files changed, 183 insertions(+), 93 deletions(-) diff --git a/example/config/application.toml b/example/config/application.toml index c0520a4e..8ab02639 100644 --- a/example/config/application.toml +++ b/example/config/application.toml @@ -1,6 +1,6 @@ name = "example" # Define a default target that will be used by a new workspace -target = "falcon" +target = ["falcon"] # Define a default environment that will be used by a new workspace #environment = "v93k" # To make your application workspace run in production mode by default diff --git a/rust/origen/cli/src/bin.rs b/rust/origen/cli/src/bin.rs index 17bcd01c..6638d200 100644 --- a/rust/origen/cli/src/bin.rs +++ b/rust/origen/cli/src/bin.rs @@ -124,9 +124,6 @@ fn main() { .subcommand(SubCommand::with_name("target") .about("Set/view the default target") .visible_alias("t") - // .arg(Arg::with_name("targets") - // .help("The name of the file(s) from targets/ to be set, added, or removed to the default target(s)") - // ) .subcommand(SubCommand::with_name("add") .about("Activates the given target(s)") .visible_alias("a") @@ -168,25 +165,6 @@ fn main() { .about("Views the currently activated target(s)") .visible_alias("v") ) - - // .arg(Arg::with_name("add") - // .help("Adds the given target(s) to the current default target(s)") - // .short("a") - // .long("add") - // .takes_value(false) - // .conflicts_with("remove") - // ) - // .arg(Arg::with_name("remove") - // .help("Removes the given target(s) to the current default target(s)") - // .short("r") - // .long("remove") - // .takes_value(false) - // .conflicts_with("add") - // ) - // .arg(Arg::with_name("clear") - // .help("Clears all the current targets") - // .short("") - // ) ) /************************************************************************************/ @@ -251,31 +229,19 @@ fn main() { ); } Some("target") => { - // let m = matches.subcommand_matches("target").unwrap(); - // let a; - // if m.value_of("add").is_some() { - // a = Some("add"); - // } else if m.value_of("remove").is_some() { - // a = Some("remove"); - // } else { - // a = None; - // } - // //commands::target::run(matches.value_of("target")); - // if let Some(targets) = m.values_of("targets") { - // commands::target::run(Some(targets.collect()), a); - // } else { - // commands::target::run(None, a); - // } let m = matches.subcommand_matches("target").unwrap(); - commands::target::run( - m.subcommand_name(), - { - match m.values_of("targets") { + let subm = m.subcommand(); + if let Some(s) = subm.1 { + commands::target::run( + Some(subm.0), + match s.values_of("targets") { Some(targets) => Some(targets.collect()), - None => None, + None => None } - } - ); + ) + } else { + commands::target::run(None, None); + } } Some("mode") => { let matches = matches.subcommand_matches("mode").unwrap(); diff --git a/rust/origen/cli/src/commands/mode.rs b/rust/origen/cli/src/commands/mode.rs index 611a25b7..d9456494 100644 --- a/rust/origen/cli/src/commands/mode.rs +++ b/rust/origen/cli/src/commands/mode.rs @@ -1,9 +1,9 @@ use origen::core::application::target; -use origen::{clean_mode, APPLICATION_CONFIG}; +use origen::{clean_mode, app_config}; pub fn run(mname: Option<&str>) { if mname.is_none() { - println!("{}", APPLICATION_CONFIG.mode); + println!("{}", app_config().mode); } else { let name = mname.unwrap(); if name == "default" { diff --git a/rust/origen/cli/src/commands/target.rs b/rust/origen/cli/src/commands/target.rs index b6a86b7b..eb394db2 100644 --- a/rust/origen/cli/src/commands/target.rs +++ b/rust/origen/cli/src/commands/target.rs @@ -1,39 +1,35 @@ use origen::core::application::target; -use origen::APPLICATION_CONFIG; +use origen::{backend_fail, backend_expect}; -<<<<<<< HEAD pub fn run(subcmd: Option<&str>, tnames: Option>) { - if subcmd.is_none() { - run(Some("view"), None) -======= -pub fn run(tnames: Option>, _action: Option<&str>) { - if tnames.is_none() { - if APPLICATION_CONFIG.target.is_some() { - let name = APPLICATION_CONFIG.target.clone().unwrap(); - println!("{}", name); - } else { - println!("No default target is currently enabled in this workspace"); - } ->>>>>>> origin/tester_prototype - } else { - if tnames.is_none() { - if APPLICATION_CONFIG.target.is_some() { - let name = APPLICATION_CONFIG.target.clone().unwrap(); - println!("{}", name); - } else { - println!("No default target is currently enabled in this workspace"); - } - } else { - for name in tnames.unwrap().iter() { - //let name = tname.unwrap(); - if name == &"default" { - target::delete_val("target"); + if let Some(cmd) = subcmd { + match cmd { + "add" => { + target::add(backend_expect!(tnames, "No targets given to 'target add' cmd!")); + }, + "default" => { + target::reset(); + }, + "remove" => { + target::remove(backend_expect!(tnames, "No targets given to 'target add' cmd!")); + }, + "set" => { + target::set(backend_expect!(tnames, "No targets given to 'target set' cmd!")); + }, + "view" => { + if let Some(targets) = target::get() { + println!("The targets currently enabled are:"); + println!("{}", targets.join("\n")) } else { - let c = target::clean_name(name, "targets", false); - target::set_workspace("target", &c); - println!("Your workspace target is now set to: {}", c); + println!("No targets have been enabled and this workspace does not enable any default targets") } + return () + } + _ => { + // Shouldn't hit this. Should be caught by clap before getting here + backend_fail!("Unknown subcommand in target processor"); } } } + run(Some("view"), None) } diff --git a/rust/origen/src/core/application/config.rs b/rust/origen/src/core/application/config.rs index 73b50f7c..ca49c3b4 100644 --- a/rust/origen/src/core/application/config.rs +++ b/rust/origen/src/core/application/config.rs @@ -1,6 +1,6 @@ use crate::core::term; /// Exposes the application configuration options from config/application.toml -/// which will include the currently selected target/environment settings form the workspace +/// which will include the currently selected target settings form the workspace use crate::STATUS; use config::File; use std::path::PathBuf; @@ -12,20 +12,26 @@ use std::path::PathBuf; // * add an example of it to src/app_generators/templates/app/config/application.toml pub struct Config { pub name: String, - pub target: Option, - pub environment: Option, + pub target: Option>, pub mode: String, } +impl Config { + pub fn refresh(&mut self) { + let latest = Self::default(); + self.name = latest.name; + self.target = latest.target; + self.mode = latest.mode; + } +} + impl Default for Config { fn default() -> Config { let mut s = config::Config::new(); // Start off by specifying the default values for all attributes, seems fine // not to handle these errors - //let _ = s.set_default("name", ""); - //let _ = s.set_default("target", None::); - //let _ = s.set_default("environment", None::); + let _ = s.set_default("target", None::>); let _ = s.set_default("mode", "development".to_string()); if STATUS.is_app_present { diff --git a/rust/origen/src/core/application/target.rs b/rust/origen/src/core/application/target.rs index 2d363785..f7b884a8 100644 --- a/rust/origen/src/core/application/target.rs +++ b/rust/origen/src/core/application/target.rs @@ -7,7 +7,7 @@ //! The target can be further overridden for a particular origen command invocation via //! the -t and -e options, or programmatically within the application code, however that is all //! handled on the front end in Python code. -use crate::STATUS; +use crate::{STATUS, app_config}; use std::path::PathBuf; use walkdir::WalkDir; // Can be used to turn a relative path in an absolute @@ -24,7 +24,7 @@ pub fn clean_name(name: &str, dir: &str, return_file: bool) -> String { let t = dir.trim_end_matches("s"); if matches.len() == 0 { - println!("No matching {} found, here are the available {}s:", t, t); + println!("No matching {} '{}' found, here are the available {}s:", t, name, t); for file in all(dir).iter() { println!( " {}", @@ -33,8 +33,9 @@ pub fn clean_name(name: &str, dir: &str, return_file: bool) -> String { } } else if matches.len() > 1 { println!( - "That {} name is ambiguous, please try again to narrow it down to one of these:", - t + "That {} name '{}' is ambiguous, please try again to narrow it down to one of these:", + t, + name ); for file in matches.iter() { println!( @@ -88,6 +89,68 @@ pub fn matches(name: &str, dir: &str) -> Vec { files } +/// Gets the currently enabled targets +pub fn get() -> Option> { + app_config().refresh(); + match app_config().target.as_ref() { + Some(targets) => Some(targets.iter().map( |t| clean_name(t, "targets", true)).collect::>().clone()), + None => None, + } +} + +/// Sets the targets, overriding any that may be present +pub fn set(targets: Vec<&str>) { + let clean_targets: Vec = targets.iter().map( |t| clean_name(t, "targets", true)).collect(); + set_workspace_array("target", clean_targets) +} + +/// Resets (deletes) the target back to its default value +pub fn reset() { + delete_val("target") +} + +/// Enables additional targets in the workspace +pub fn add(targets: Vec<&str>) { + let mut current: Vec = match &app_config().target { + Some(targets) => targets.clone(), + None => vec![], + }.iter().map( |t| clean_name(t, "targets", true)).collect(); + + for t in targets.iter() { + // Check that the targets to add are valid + let clean_t = clean_name(t, "targets", true); + + // If the target is already added, remove it from its current position and reapply it in the order + // given here + current.retain( |c| *c != clean_t); + current.push(clean_t); + } + + set_workspace_array("target", current); +} + +/// Disables currently enables targets in the workspace +pub fn remove(targets: Vec<&str>) { + let mut current: Vec = match &app_config().target { + Some(targets) => targets.clone(), + None => vec![], + }.iter().map( |t| clean_name(t, "targets", true)).collect(); + + for t in targets.iter() { + let clean_t = clean_name(t, "targets", true); + + // Remove the target, if present + current.retain( |c| *c != clean_t); + } + + if current.len() == 0 { + println!("All targets were removed. Resetting to the default target."); + reset(); + } else { + set_workspace_array("target", current); + } +} + /// Returns all files from the given directory pub fn all(dir: &str) -> Vec { let mut files: Vec = Vec::new(); @@ -109,6 +172,13 @@ pub fn set_workspace(key: &str, val: &str) { add_val(key, val); } +/// Sets an Array-of-Strings workspace variable +pub fn set_workspace_array(key: &str, vals: Vec) { + ensure_app_dot_toml(); + delete_val(key); + add_val_array(key, vals); +} + /// Deletes the given key (and its val) from .origen/application.toml if it exists pub fn delete_val(key: &str) { let path = STATUS.root.join(".origen").join("application.toml"); @@ -134,6 +204,20 @@ fn add_val(key: &str, val: &str) { .expect("Unable to write file .origen/application.toml!"); } +/// Appends the given key/val pair to the end of .origen/application.toml +fn add_val_array(key: &str, vals: Vec) { + let path = STATUS.root.join(".origen").join("application.toml"); + let data = fs::read_to_string(path).expect("Unable to read file .origen/application.toml"); + + // Note: use string literals here to account for Windows paths + let new_data = format!("{}\n{} = [{}]", data.trim(), key, vals.iter().map( |v| format!("'{}'", v)).collect::>().join(", ")); + fs::write( + STATUS.root.join(".origen").join("application.toml"), + new_data, + ) + .expect("Unable to write file .origen/application.toml!"); +} + /// Verifies that .origen/application.toml exists and if not creates one fn ensure_app_dot_toml() { let path = STATUS.root.join(".origen"); diff --git a/rust/origen/src/core/mod.rs b/rust/origen/src/core/mod.rs index 0feb7364..e8ee104c 100644 --- a/rust/origen/src/core/mod.rs +++ b/rust/origen/src/core/mod.rs @@ -6,5 +6,7 @@ pub mod model; pub mod os; pub mod status; pub mod term; + +#[macro_use] pub mod utility; pub mod tester; diff --git a/rust/origen/src/core/utility/logger.rs b/rust/origen/src/core/utility/logger.rs index a7d67fe8..4b6ab77a 100644 --- a/rust/origen/src/core/utility/logger.rs +++ b/rust/origen/src/core/utility/logger.rs @@ -7,6 +7,35 @@ use std::fs; use std::io::Write; use std::path::PathBuf; +#[macro_export] +macro_rules! backend_fail { + ($message:expr) => { + origen::LOGGER.error(&format!("A problem occurred in the Origen backend: '{}'", $message)); + std::process::exit(1); + }; +} + +#[macro_export] +macro_rules! backend_expect { + ($obj:expr, $message:expr) => {{ + match $obj { + Some(o) => o, + None => { + origen::LOGGER.error(&format!("A problem occurred in the Origen backend as an Error or None value was unwrapped: '{}'", $message)); + std::process::exit(1); + } + } + }}; +} + +#[macro_export] +macro_rules! fail { + ($message:expr) => { + origen::LOGGER.error($message); + std::process::exit(1); + }; +} + pub struct Logger { pub output_file: PathBuf, file_handler: fs::File, diff --git a/rust/origen/src/core/utility/mod.rs b/rust/origen/src/core/utility/mod.rs index ed9a7cbf..26467964 100644 --- a/rust/origen/src/core/utility/mod.rs +++ b/rust/origen/src/core/utility/mod.rs @@ -1,2 +1,4 @@ pub mod big_uint_helpers; + +#[macro_use] pub mod logger; diff --git a/rust/origen/src/lib.rs b/rust/origen/src/lib.rs index 33d4bc26..5c0669eb 100644 --- a/rust/origen/src/lib.rs +++ b/rust/origen/src/lib.rs @@ -39,7 +39,7 @@ lazy_static! { pub static ref ORIGEN_CONFIG: OrigenConfig = OrigenConfig::default(); /// Provides configuration information derived from application.toml and any workspace /// overrides e.g. from running origen t command to set a default target - pub static ref APPLICATION_CONFIG: AppConfig = AppConfig::default(); + pub static ref APPLICATION_CONFIG: Mutex = Mutex::new(AppConfig::default()); pub static ref LOGGER: Logger = Logger::default(); /// The current device model, containing all metadata about hierarchy, regs, pins, specs, /// timing, etc. and responsible for maintaining the current state of the DUT (regs, pins, @@ -94,6 +94,10 @@ pub fn services() -> MutexGuard<'static, Services> { SERVICES.lock().unwrap() } +pub fn app_config() -> MutexGuard<'static, AppConfig> { + APPLICATION_CONFIG.lock().unwrap() +} + /// Sanitizes the given mode string and returns it, but will exit the process if it is invalid pub fn clean_mode(name: &str) -> String { let mut matches: Vec = Vec::new(); diff --git a/rust/pyapi/src/lib.rs b/rust/pyapi/src/lib.rs index f795e805..853a6eb4 100644 --- a/rust/pyapi/src/lib.rs +++ b/rust/pyapi/src/lib.rs @@ -12,7 +12,8 @@ mod tester; use crate::registers::bit_collection::BitCollection; use num_bigint::BigUint; -use origen::{Dut, Error, Result, Value, APPLICATION_CONFIG, ORIGEN_CONFIG, STATUS, TEST}; +use origen::{Dut, Error, Result, Value, ORIGEN_CONFIG, STATUS, TEST}; +use origen::app_config as origen_app_config; use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; use pyo3::{wrap_pyfunction, wrap_pymodule}; @@ -116,10 +117,10 @@ fn config(py: Python) -> PyResult { fn app_config(py: Python) -> PyResult { let ret = PyDict::new(py); // Don't think an error can really happen here, so not handled - let _ = ret.set_item("name", &APPLICATION_CONFIG.name); - let _ = ret.set_item("target", &APPLICATION_CONFIG.target); - let _ = ret.set_item("environment", &APPLICATION_CONFIG.environment); - let _ = ret.set_item("mode", &APPLICATION_CONFIG.mode); + let app_config = origen_app_config(); + let _ = ret.set_item("name", &app_config.name); + let _ = ret.set_item("target", &app_config.target); + let _ = ret.set_item("mode", &app_config.mode); Ok(ret.into()) }