├── LICENSE ├── README.md ├── csv_parser.svh ├── lib ├── csv_parser.sv ├── csv_title_reader.sv └── sv_coverage.sv └── unittest ├── cases ├── run_all_test.sv ├── test_load_csv.csv ├── test_load_csv_2.csv ├── test_load_csv_empty.csv ├── test_load_csv_quote_0.csv ├── test_load_csv_quote_1.csv ├── test_load_csv_quote_2.csv ├── test_load_csv_quote_3.csv ├── test_load_csv_quote_4.csv ├── test_title_reader_0.csv ├── test_title_reader_err_0.csv ├── ut_basic_quoted.sv ├── ut_basic_unquoted.sv └── ut_title_reader.sv ├── sim ├── clr.sh ├── cover_need.txt ├── flist ├── run.sh ├── synopsys_sim.setup ├── vlogana_src.sh └── vlogana_uvm.sh └── unit_test ├── lib ├── mjolnir_test.sv ├── mjolnir_testcase.sv └── mjolnir_testsuit.sv ├── unit_test.sv └── unit_test.svh /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sv_csv_parser 2 | A CSV file parser, written in SystemVerilog 3 | 4 | # How to use csv_parser: 5 | ```systemverilog 6 | csv_record record_row; 7 | csv_parser parser = new(); 8 | 9 | // Quit if load fails 10 | assert(!parser.load_csv_file(csv_file_name)) else ...; 11 | 12 | // Quit if parsing fails 13 | assert(parser.parse() == CSV_SUCCESS) else ...; 14 | 15 | 16 | // Iterate each field of the table 17 | for(int i=0;i < parser.size();i ++) begin 18 | record_row = parser.get_record(.row_index(i)); 19 | for(int j=0;j < record_row.size();j ++) 20 | field = record_row.get_field(.field_index(j)); 21 | end 22 | ``` 23 | 24 | # About csv_title_reader 25 | Class **csv_title_reader** reads, parses given csv file, and builds a **title-value** mapping according to the contents of csv file, 26 | with recognizing fields of first row as titles for the values below them. 27 | 28 | 29 | As an example, for a CSV file: 30 | ``` 31 | ID, Name, Age, 32 | 0, John, 11 33 | 1, Joe, 12 34 | ``` 35 | 36 | After parsing of **csv_title_reader**, codes as followings would be written for accessing a field: 37 | 38 | ```systemverilog 39 | csv_table_with_title the_tab; 40 | csv_title_reader title_rdr = new(); 41 | string val; 42 | 43 | the_tab = title_rdr.read_n_parse_csv("A CSV File.csv"); 44 | assert(the_tab.get_field(.row_no(1), .field_name("Name"), .value(val)) else ...; 45 | // val == "Joe" 46 | ``` 47 | -------------------------------------------------------------------------------- /csv_parser.svh: -------------------------------------------------------------------------------- 1 | // A Tiny CSV file parser, in SystemVerilog 2 | // Copyright (C) 2015 Mo Chen 3 | // 4 | // This library is free; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 3.0 of the License, or (at your option) any later version. 8 | 9 | `ifndef CSV_PARSER_PREFIX 10 | `define CSV_PARSER_PREFIX . 11 | `endif 12 | 13 | `ifndef IN_OTHER_PKG 14 | `define AUTO_BUILD_INC_PATH(T, ORIGINAL_PATH) `"``T``/``ORIGINAL_PATH```" 15 | 16 | // This file contains a package named 'csv_parser_pkg' 17 | package csv_parser_pkg; 18 | `endif 19 | 20 | `include `AUTO_BUILD_INC_PATH(`CSV_PARSER_PREFIX, lib/sv_coverage.sv) 21 | `include `AUTO_BUILD_INC_PATH(`CSV_PARSER_PREFIX, lib/csv_parser.sv) 22 | `include `AUTO_BUILD_INC_PATH(`CSV_PARSER_PREFIX, lib/csv_title_reader.sv) 23 | 24 | `ifndef IN_OTHER_PKG 25 | endpackage 26 | 27 | `undef AUTO_BUILD_INC_PATH 28 | `endif 29 | 30 | -------------------------------------------------------------------------------- /lib/csv_parser.sv: -------------------------------------------------------------------------------- 1 | // Author: Mo Chen 2 | // May 22, 2015 3 | 4 | // Error Codes 5 | typedef enum int { 6 | CSV_SUCCESS =0, 7 | CSV_EPARSE =1, //Parse error in strict mode 8 | CSV_EINVALID=2 //Invalid code,should never be received from csv_error 9 | } CSV_RETURN_VAL; 10 | 11 | 12 | //* Character values 13 | typedef enum bit [7:0] { 14 | CSV_TAB = 8'h09, 15 | CSV_SPACE = 8'h20, 16 | CSV_CR = 8'h0d, 17 | CSV_LF = 8'h0a, 18 | CSV_COMMA = 8'h2c, 19 | CSV_QUOTE = 8'h22 20 | } CSV_CHARACTERS; 21 | 22 | 23 | typedef enum int { 24 | ROW_NOT_BEGUN =0, 25 | FIELD_NOT_BEGUN =1, 26 | FIELD_BEGUN =2, 27 | FIELD_MIGHT_HAVE_ENDED =3 28 | } CSV_PARSE_STATE; 29 | 30 | 31 | class csv_record; 32 | protected string fields[$]; 33 | 34 | virtual function void push_back(string field); 35 | fields.push_back(field); 36 | endfunction 37 | 38 | virtual function string get_field(int unsigned field_index); 39 | return fields[field_index]; 40 | endfunction 41 | 42 | virtual function int unsigned size(); 43 | return fields.size(); 44 | endfunction 45 | endclass 46 | 47 | 48 | class csv_parser; 49 | string file_buffer[$];// File buffer for the raw contents from csv 50 | protected csv_record record_rows[$]; 51 | 52 | // Configurations 53 | protected CSV_PARSE_STATE state = ROW_NOT_BEGUN; 54 | protected bit [7:0] delim = CSV_COMMA; 55 | protected bit [7:0] quote = CSV_QUOTE; 56 | 57 | // Local variables used in parsing 58 | protected string a_field = ""; 59 | protected int quoted = 0, spaces = 0, entry_pos = 0; 60 | 61 | function new(); 62 | endfunction 63 | 64 | // To make the user know how many functions are supplied by csv_parser, 65 | // extern function definition is used here. 66 | 67 | // Public Functions 68 | extern function int load_csv_file(string file_name); 69 | extern function CSV_RETURN_VAL parse(); 70 | extern function csv_record get_record(int unsigned row_index=0); 71 | extern virtual function int unsigned size(); 72 | 73 | // Private Functions 74 | extern protected virtual function int row_not_begun_field_not_begun_proc(bit [7:0] c); 75 | extern protected virtual function CSV_RETURN_VAL field_begun_proc(bit [7:0] c); 76 | extern protected virtual function CSV_RETURN_VAL field_might_have_ended_proc(bit [7:0] c); 77 | 78 | extern protected virtual function void submit_row(bit [7:0] c); 79 | extern protected virtual function void submit_field(); 80 | extern protected virtual function void submit_char(bit [7:0] c); 81 | 82 | endclass 83 | 84 | 85 | function int csv_parser::load_csv_file(string file_name); 86 | int fd; 87 | string str; 88 | 89 | fd = $fopen(file_name,"r"); 90 | if(fd == 0) begin 91 | $display("Fatal Error:Can NOT find the %s to open",file_name); 92 | return 1; 93 | end 94 | file_buffer.delete(); 95 | 96 | while ($fgets(str, fd) > 0) begin 97 | if (str == "") 98 | continue; 99 | file_buffer.push_back(str); 100 | end 101 | $fclose(fd); 102 | return 0; 103 | endfunction 104 | 105 | function CSV_RETURN_VAL csv_parser::parse(); 106 | int row=0, col=0; 107 | bit [7:0] c; 108 | CSV_RETURN_VAL ret_code; 109 | 110 | quoted = 0; 111 | record_rows.delete(); 112 | 113 | foreach(file_buffer[row]) begin 114 | foreach(file_buffer[row][col]) begin 115 | c = file_buffer[row][col]; 116 | 117 | case(state) 118 | ROW_NOT_BEGUN, FIELD_NOT_BEGUN: 119 | if(row_not_begun_field_not_begun_proc(c)) 120 | continue; 121 | FIELD_BEGUN: begin 122 | ret_code = field_begun_proc(c); 123 | if(ret_code != CSV_SUCCESS) 124 | return ret_code; 125 | end 126 | FIELD_MIGHT_HAVE_ENDED: begin 127 | ret_code = field_might_have_ended_proc(c); 128 | if(ret_code != CSV_SUCCESS) 129 | return ret_code; 130 | end 131 | endcase 132 | end 133 | end 134 | return CSV_SUCCESS; 135 | endfunction 136 | 137 | function csv_record csv_parser::get_record(int unsigned row_index=0); 138 | return record_rows[row_index]; 139 | endfunction 140 | 141 | function int unsigned csv_parser::size(); 142 | int row_cnt = 0; 143 | 144 | // Find the first empty row, and only rows before it counts 145 | foreach(record_rows[i]) 146 | if(record_rows[i].size() == 0) begin 147 | row_cnt = i ; 148 | break; 149 | end 150 | return row_cnt; 151 | endfunction 152 | 153 | function int csv_parser::row_not_begun_field_not_begun_proc(bit [7:0] c); 154 | if((c == CSV_SPACE || c == CSV_TAB) && c!=delim) 155 | return 1; // Continue 156 | else if (c == CSV_CR || c == CSV_LF) begin // Carriage Return or Line Feed 157 | if(state == FIELD_NOT_BEGUN) begin 158 | `COVER(submit_field();) 159 | `COVER(submit_row(c);) 160 | end 161 | // Don't submit empty rows by default 162 | return 1; // Continue 163 | end 164 | else if (c == delim) // Comma 165 | `COVER(submit_field();) 166 | else if (c == quote) begin // Quote 167 | `COVER(state = FIELD_BEGUN;) 168 | quoted = 1; 169 | end 170 | else begin // Anything else 171 | `COVER(state = FIELD_BEGUN;) 172 | quoted = 0; 173 | submit_char(c); 174 | end 175 | 176 | return 0; 177 | endfunction 178 | 179 | function CSV_RETURN_VAL csv_parser::field_begun_proc(bit [7:0] c); 180 | if (c == quote) begin // Quote 181 | if (quoted) begin 182 | `COVER(submit_char(c);) 183 | state = FIELD_MIGHT_HAVE_ENDED; 184 | end 185 | else // STRICT ERROR - double quote inside non-quoted field 186 | `COVER(return CSV_EPARSE;) 187 | end 188 | else if (c == delim) begin // Comma 189 | if (quoted) 190 | `COVER(submit_char(c);) 191 | else 192 | `COVER(submit_field();) 193 | end 194 | else if (c == CSV_CR || c == CSV_LF) begin // Carriage Return or Line Feed 195 | if (!quoted) begin 196 | `COVER(submit_field();) 197 | submit_row(c); 198 | end 199 | else 200 | `COVER(submit_char(c);) 201 | end 202 | else if (!quoted && (c == CSV_SPACE || c == CSV_TAB)) begin // Tab or space for non-quoted field 203 | `COVER(submit_char(c);) 204 | spaces++; 205 | end 206 | else begin // Anything else 207 | `COVER(submit_char(c);) 208 | spaces = 0; 209 | end 210 | return CSV_SUCCESS; 211 | endfunction 212 | 213 | function CSV_RETURN_VAL csv_parser::field_might_have_ended_proc(bit [7:0] c); 214 | // This only happens when a quote character is encountered in a quoted field 215 | if (c == delim) begin // Comma 216 | `COVER(entry_pos -= spaces + 1;) // get rid of spaces and original quote 217 | submit_field(); 218 | end 219 | else if (c == CSV_CR || c == CSV_LF) begin // Carriage Return or Line Feed 220 | `COVER(entry_pos -= spaces + 1;) // get rid of spaces and original quote 221 | submit_field(); 222 | submit_row(c); 223 | end 224 | else if (c == CSV_SPACE || c == CSV_TAB) begin // Space or Tab 225 | `COVER(submit_char(c);) 226 | spaces++; 227 | end 228 | else if (c == quote) begin // Quote 229 | if (spaces) 230 | // STRICT ERROR - unescaped double quote 231 | `COVER(return CSV_EPARSE;) 232 | else 233 | // Two quotes in a row 234 | `COVER(state = FIELD_BEGUN;) 235 | end 236 | else begin // Anything else 237 | // STRICT ERROR - unescaped double quote 238 | `COVER(return CSV_EPARSE;) 239 | end 240 | return CSV_SUCCESS; 241 | endfunction 242 | 243 | function void csv_parser::submit_row(bit [7:0] c); 244 | csv_record new_row; 245 | 246 | new_row = new(); 247 | record_rows.push_back(new_row); 248 | 249 | state = ROW_NOT_BEGUN; 250 | entry_pos = 0; 251 | quoted = 0; 252 | spaces = 0; 253 | endfunction 254 | 255 | function void csv_parser::submit_field(); 256 | csv_record new_row; 257 | csv_record fields; 258 | string field_being_pushed = ""; 259 | 260 | if(record_rows.size() == 0) begin 261 | new_row = new(); 262 | record_rows.push_back(new_row); 263 | end 264 | 265 | fields = record_rows[$]; 266 | if(!quoted) 267 | entry_pos -= spaces; 268 | for(int i=0;i < entry_pos;i ++) 269 | field_being_pushed = {field_being_pushed, a_field[i]}; 270 | fields.push_back(field_being_pushed); 271 | a_field = ""; 272 | 273 | state = FIELD_NOT_BEGUN; 274 | entry_pos = 0; 275 | quoted = 0; 276 | spaces = 0; 277 | endfunction 278 | 279 | function void csv_parser::submit_char(bit [7:0] c); 280 | string s = string'(c); 281 | a_field = {a_field, s}; 282 | entry_pos ++; 283 | endfunction 284 | 285 | -------------------------------------------------------------------------------- /lib/csv_title_reader.sv: -------------------------------------------------------------------------------- 1 | // Author: Mo Chen 2 | // May 22, 2015 3 | 4 | class csv_table_with_title; 5 | string titles[$]; 6 | 7 | protected int unsigned title_to_index_mapping[string]; 8 | protected csv_record record_rows[$]; 9 | 10 | function int get_field(int unsigned row_no, string field_name, ref string value); 11 | csv_record record; 12 | 13 | // Illegal title is given 14 | if(!title_to_index_mapping.exists(field_name)) 15 | return 0; 16 | 17 | // Row index out-of-range 18 | if(record_rows.size() <= row_no) 19 | return 0; 20 | 21 | record = record_rows[row_no]; 22 | // Column index out-of-range 23 | if(title_to_index_mapping[field_name] >= record.size()) 24 | return 0; 25 | value = record.get_field(title_to_index_mapping[field_name]); 26 | return 1; 27 | endfunction 28 | 29 | function int unsigned size(); 30 | return record_rows.size(); 31 | endfunction 32 | 33 | virtual function void push_back(csv_record record); 34 | record_rows.push_back(record); 35 | endfunction 36 | 37 | function int append_title(string title, int unsigned column); 38 | titles.push_back(title); 39 | title_to_index_mapping[title] = column; 40 | return titles.size(); 41 | endfunction 42 | endclass 43 | 44 | 45 | class csv_title_reader; 46 | protected csv_parser parser; 47 | 48 | function new(); 49 | parser = new(); 50 | endfunction 51 | 52 | function csv_table_with_title read_n_parse_csv(string csv_file_name); 53 | csv_record the_title_of_csv, record; 54 | csv_table_with_title the_table; 55 | 56 | // Quit if load fails 57 | assert(!parser.load_csv_file(csv_file_name)) else return null; 58 | 59 | // Quit if parsing fails 60 | assert(parser.parse() == CSV_SUCCESS) else return null; 61 | 62 | the_table = new(); 63 | 64 | // Build titles of the table 65 | the_title_of_csv = parser.get_record(.row_index(0)); 66 | for(int i=0;i < the_title_of_csv.size();i ++) 67 | void'(the_table.append_title(.title(the_title_of_csv.get_field(i)), 68 | .column(i))); 69 | 70 | // Build contents of the table 71 | for(int i=1;i < parser.size();i ++) begin 72 | record = parser.get_record(.row_index(i)); 73 | the_table.push_back(record); 74 | end 75 | return the_table; 76 | endfunction 77 | endclass 78 | -------------------------------------------------------------------------------- /lib/sv_coverage.sv: -------------------------------------------------------------------------------- 1 | // Author: Mo Chen 2 | // May 22, 2015 3 | 4 | `ifdef CODE_COVERAGE_MODE 5 | `define COVER(cmd) begin sv_code_coverage_collector::submit(`__FILE__, `__LINE__, "``cmd``"); cmd end 6 | `else 7 | `define COVER(cmd) cmd 8 | `endif 9 | 10 | class sv_file_coverage; 11 | string covered_line_no[*]; 12 | int line_no_hit_times[*]; 13 | 14 | function void submit(int unsigned line_no, string the_codes); 15 | if(!covered_line_no.exists(line_no)) begin 16 | covered_line_no[line_no] = the_codes; 17 | line_no_hit_times[line_no] = 0; 18 | end 19 | line_no_hit_times[line_no] ++; 20 | endfunction 21 | 22 | function string sprint(string prefix); 23 | string s_all_lines = ""; 24 | string s; 25 | int line_no; 26 | 27 | void'(covered_line_no.first(line_no)); 28 | do begin 29 | $sformat(s, "%s, Line: %0d, Hit: %0d, Codes: %s", 30 | prefix, line_no, line_no_hit_times[line_no], covered_line_no[line_no]); 31 | s_all_lines = {s_all_lines, s, "\n"}; 32 | end 33 | while(covered_line_no.next(line_no)); 34 | 35 | return s_all_lines; 36 | endfunction 37 | endclass 38 | 39 | class sv_code_coverage_collector; 40 | static sv_file_coverage coverages[string]; 41 | 42 | static function void submit(string file_name, int unsigned line_no, string the_codes); 43 | sv_file_coverage file_coverage; 44 | if(!coverages.exists(file_name)) 45 | coverages[file_name] = new(); 46 | file_coverage = coverages[file_name]; 47 | file_coverage.submit(line_no, the_codes); 48 | endfunction 49 | 50 | static function void print(); 51 | string s = ""; 52 | string s_all_files = ""; 53 | string file_name; 54 | 55 | void'(coverages.first(file_name)); 56 | do begin 57 | s = coverages[file_name].sprint(file_name); 58 | s_all_files = {s_all_files, s, "\n"}; 59 | end 60 | while(coverages.next(file_name)); 61 | $display(s_all_files); 62 | endfunction 63 | endclass 64 | 65 | -------------------------------------------------------------------------------- /unittest/cases/run_all_test.sv: -------------------------------------------------------------------------------- 1 | `include "unit_test.svh" 2 | 3 | program TB; 4 | import mjolnir_unit_test::*; 5 | 6 | class all_tc4csv_parser extends mjolnir_testsuit; 7 | ut_basic_unquoted test_basic_unquoted; 8 | ut_basic_quoted test_basic_quoted; 9 | ut_title_reader test_title_reader; 10 | 11 | function new(string the_name=""); 12 | super.new(the_name); 13 | 14 | test_basic_unquoted = new("Test loading & parsing unquoted fields"); 15 | test_basic_quoted = new("Test loading & parsing quoted fields"); 16 | test_title_reader = new("Test the title reader"); 17 | add_test(test_basic_unquoted); 18 | add_test(test_basic_quoted); 19 | add_test(test_title_reader); 20 | endfunction 21 | endclass 22 | 23 | TEST_RUN_STATUS pass; 24 | all_tc4csv_parser my_suit; 25 | 26 | initial begin 27 | my_suit = new("The test suit for CSV Parser"); 28 | my_suit.run(pass); 29 | $display("=========================================================="); 30 | $display(my_suit.test_status()); 31 | assert(pass == PASS); 32 | $display("Test for the framework of unittest has done"); 33 | $display("=========================================================="); 34 | $display("The followings are code coverage"); 35 | sv_code_coverage_collector::print(); 36 | end 37 | endprogram 38 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv.csv: -------------------------------------------------------------------------------- 1 | 100, 200, 300, 400, haha 2 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_2.csv: -------------------------------------------------------------------------------- 1 | 100, 200, 300, 400, haha 2 | 110, 220, 330, 440, wowo 3 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_empty.csv: -------------------------------------------------------------------------------- 1 | 100, 200, 300, 400, haha 2 | 3 | 110 , 220, 330, 440, wowo 4 | 120, 230, , 450, 5 | 6 | 130, , 340, 460, heihei 7 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_quote_0.csv: -------------------------------------------------------------------------------- 1 | 100, 200, 300, 400, "haha kaka" 2 | 3 | 110, 220, 330, 440, wowo 4 | 120, 230, "", 450, """" 5 | 6 | 130, " ", "340, ""350"", " , 460, heihei 7 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_quote_1.csv: -------------------------------------------------------------------------------- 1 | 100, 200, 300, 400, "haha kaka" 2 | 3 | 110, 220, 330, 440, wowo 4 | 120, 230, " 5 | 6 | ", 450, """" 7 | 8 | 130, " ", "340, ""350"", " , 460, heihei 9 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_quote_2.csv: -------------------------------------------------------------------------------- 1 | 120, 230, "might have ended 160" ", 450, "haha" 2 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_quote_3.csv: -------------------------------------------------------------------------------- 1 | 120, 230, a", 450, "haha" 2 | -------------------------------------------------------------------------------- /unittest/cases/test_load_csv_quote_4.csv: -------------------------------------------------------------------------------- 1 | 120, 230, "might have ended 167"anything else, 450, "haha" 2 | -------------------------------------------------------------------------------- /unittest/cases/test_title_reader_0.csv: -------------------------------------------------------------------------------- 1 | title1, title2, "title 3" 2 | 11, 21, " 31" 3 | 12, 22, " 32" 4 | 13, 23, " 33" 5 | -------------------------------------------------------------------------------- /unittest/cases/test_title_reader_err_0.csv: -------------------------------------------------------------------------------- 1 | title1, title2, "title 3" 2 | 11, 21, " 31" 3 | 12, 22 4 | 13, 23, " 33" 5 | -------------------------------------------------------------------------------- /unittest/cases/ut_basic_quoted.sv: -------------------------------------------------------------------------------- 1 | class ut_basic_quoted extends mjolnir_testcase; 2 | csv_parser parser; 3 | 4 | function new(string the_name = ""); 5 | super.new(the_name); 6 | endfunction 7 | 8 | 9 | function void setup(); 10 | parser = new(); 11 | endfunction 12 | 13 | 14 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_quote_0, ut_basic_quoted) 15 | csv_record record_rows; 16 | string expt_str[4][5] = '{ 17 | '{"100", "200", "300" , "400", "haha kaka"}, 18 | '{"110", "220", "330" , "440", "wowo" }, 19 | '{"120", "230", "" , "450", "\"" }, 20 | '{"130", " " , "340, \"350\", " , "460", "heihei" }}; 21 | pass = PASS; 22 | 23 | assert(self.parser.load_csv_file("../cases/test_load_csv_quote_0.csv") == 0) else pass = FAIL; 24 | assert(self.parser.parse() == 0) else pass = FAIL; 25 | 26 | foreach(expt_str[i]) begin 27 | record_rows = self.parser.get_record(i); 28 | assert(5 === record_rows.size()); 29 | foreach(expt_str[i][j]) 30 | assert(expt_str[i][j] == record_rows.get_field(j)) else begin 31 | pass = FAIL; 32 | $display("Exptect: |%s|, Observe: |%s|", expt_str[i][j], record_rows.get_field(j)); 33 | end; 34 | end 35 | `MJOLNIR_NEW_TEST_END(test_parse_quote_0) 36 | 37 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_quote_with_cr_in_quoted, ut_basic_quoted) 38 | csv_record record_rows; 39 | string expt_str[4][5] = '{ 40 | '{"100", "200", "300" , "400", "haha kaka"}, 41 | '{"110", "220", "330" , "440", "wowo" }, 42 | '{"120", "230", "\n\n" , "450", "\"" }, 43 | '{"130", " " , "340, \"350\", " , "460", "heihei" }}; 44 | pass = PASS; 45 | 46 | assert(self.parser.load_csv_file("../cases/test_load_csv_quote_1.csv") == 0) else pass = FAIL; 47 | assert(self.parser.parse() == 0) else pass = FAIL; 48 | 49 | foreach(expt_str[i]) begin 50 | record_rows = self.parser.get_record(i); 51 | assert(5 === record_rows.size()); 52 | foreach(expt_str[i][j]) 53 | assert(expt_str[i][j] == record_rows.get_field(j)) else begin 54 | pass = FAIL; 55 | $display("Exptect: |%s|, Observe: |%s|", expt_str[i][j], record_rows.get_field(j)); 56 | end; 57 | end 58 | `MJOLNIR_NEW_TEST_END(test_parse_quote_with_cr_in_quoted) 59 | 60 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_quote_err_0, ut_basic_quoted) 61 | csv_record record_rows; 62 | pass = PASS; 63 | 64 | assert(self.parser.load_csv_file("../cases/test_load_csv_quote_2.csv") == 0) else pass = FAIL; 65 | assert(self.parser.parse() > 0) else pass = FAIL; 66 | `MJOLNIR_NEW_TEST_END(test_parse_quote_err_0) 67 | 68 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_quote_err_1, ut_basic_quoted) 69 | csv_record record_rows; 70 | pass = PASS; 71 | 72 | assert(self.parser.load_csv_file("../cases/test_load_csv_quote_3.csv") == 0) else pass = FAIL; 73 | assert(self.parser.parse() > 0) else pass = FAIL; 74 | `MJOLNIR_NEW_TEST_END(test_parse_quote_err_1) 75 | 76 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_quote_err_2, ut_basic_quoted) 77 | csv_record record_rows; 78 | pass = PASS; 79 | 80 | assert(self.parser.load_csv_file("../cases/test_load_csv_quote_4.csv") == 0) else pass = FAIL; 81 | assert(self.parser.parse() > 0) else pass = FAIL; 82 | `MJOLNIR_NEW_TEST_END(test_parse_quote_err_2) 83 | endclass 84 | -------------------------------------------------------------------------------- /unittest/cases/ut_basic_unquoted.sv: -------------------------------------------------------------------------------- 1 | import csv_parser_pkg::*; 2 | import uvm_pkg::*; 3 | import mjolnir_unit_test::*; 4 | 5 | class ut_basic_unquoted extends mjolnir_testcase; 6 | csv_parser parser; 7 | 8 | function new(string the_name = ""); 9 | super.new(the_name); 10 | endfunction 11 | 12 | 13 | function void setup(); 14 | parser = new(); 15 | endfunction 16 | 17 | 18 | `MJOLNIR_NEW_TEST_BEGIN(test_load_csv, ut_basic_unquoted) 19 | string expt_str = "100, 200, 300, 400, haha\n"; 20 | pass = PASS; 21 | 22 | assert(self.parser.load_csv_file("../cases/test_load_csv_unexisted.csv") == 1) else pass = FAIL; 23 | assert(self.parser.load_csv_file("../cases/test_load_csv.csv") == 0) else pass = FAIL; 24 | assert(self.parser.file_buffer.size() == 1) else pass = FAIL; 25 | $display("Expect: %s", expt_str); 26 | $display("Observe: %s", self.parser.file_buffer[0]); 27 | assert(expt_str == self.parser.file_buffer[0]) else pass = FAIL; 28 | `MJOLNIR_NEW_TEST_END(test_load_csv) 29 | 30 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_0, ut_basic_unquoted) 31 | csv_record record_rows; 32 | string expt_str[] = '{"100", "200", "300", "400", "haha"}; 33 | pass = PASS; 34 | 35 | assert(self.parser.load_csv_file("../cases/test_load_csv.csv") == 0) else pass = FAIL; 36 | assert(self.parser.parse() == 0) else pass = FAIL; 37 | 38 | record_rows = self.parser.get_record(0); 39 | assert(expt_str.size() === record_rows.size()); 40 | foreach(expt_str[i]) 41 | assert(expt_str[i] == record_rows.get_field(i)) else pass = FAIL; 42 | `MJOLNIR_NEW_TEST_END(test_parse_0) 43 | 44 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_1, ut_basic_unquoted) 45 | csv_record record_rows; 46 | string expt_str[2][5] = '{'{"100", "200", "300", "400", "haha"}, 47 | '{"110", "220", "330", "440", "wowo"}}; 48 | pass = PASS; 49 | 50 | assert(self.parser.load_csv_file("../cases/test_load_csv_2.csv") == 0) else pass = FAIL; 51 | assert(self.parser.parse() == 0) else pass = FAIL; 52 | 53 | foreach(expt_str[i]) begin 54 | record_rows = self.parser.get_record(i); 55 | assert(5 === record_rows.size()); 56 | foreach(expt_str[i][j]) 57 | assert(expt_str[i][j] == record_rows.get_field(j)) else pass = FAIL; 58 | end 59 | `MJOLNIR_NEW_TEST_END(test_parse_1) 60 | 61 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_empty, ut_basic_unquoted) 62 | csv_record record_rows; 63 | string expt_str[4][5] = '{ 64 | '{"100", "200", "300", "400", "haha"}, 65 | '{"110", "220", "330", "440", "wowo"}, 66 | '{"120", "230", "" , "450", "" }, 67 | '{"130", "", "340", "460", "heihei"}}; 68 | pass = PASS; 69 | 70 | assert(self.parser.load_csv_file("../cases/test_load_csv_empty.csv") == 0) else pass = FAIL; 71 | assert(self.parser.parse() == 0) else pass = FAIL; 72 | 73 | foreach(expt_str[i]) begin 74 | record_rows = self.parser.get_record(i); 75 | assert(5 === record_rows.size()); 76 | foreach(expt_str[i][j]) 77 | assert(expt_str[i][j] == record_rows.get_field(j)) else pass = FAIL; 78 | end 79 | `MJOLNIR_NEW_TEST_END(test_parse_empty) 80 | 81 | `MJOLNIR_NEW_TEST_BEGIN(test_parse_run_twice, ut_basic_unquoted) 82 | csv_record record_rows; 83 | string expt_str_0[4][5] = '{ 84 | '{"100", "200", "300", "400", "haha"}, 85 | '{"110", "220", "330", "440", "wowo"}, 86 | '{"120", "230", "" , "450", "" }, 87 | '{"130", "", "340", "460", "heihei"}}; 88 | string expt_str_1[2][5] = '{'{"100", "200", "300", "400", "haha"}, 89 | '{"110", "220", "330", "440", "wowo"}}; 90 | pass = PASS; 91 | 92 | // The round One 93 | assert(self.parser.load_csv_file("../cases/test_load_csv_empty.csv") == 0) else pass = FAIL; 94 | assert(self.parser.parse() == 0) else pass = FAIL; 95 | 96 | foreach(expt_str_0[i]) begin 97 | record_rows = self.parser.get_record(i); 98 | assert(5 === record_rows.size()); 99 | foreach(expt_str_0[i][j]) 100 | assert(expt_str_0[i][j] == record_rows.get_field(j)) else pass = FAIL; 101 | end 102 | 103 | // The round Two 104 | assert(self.parser.load_csv_file("../cases/test_load_csv_2.csv") == 0) else pass = FAIL; 105 | assert(self.parser.parse() == 0) else pass = FAIL; 106 | 107 | foreach(expt_str_1[i]) begin 108 | record_rows = self.parser.get_record(i); 109 | assert(5 === record_rows.size()); 110 | foreach(expt_str_1[i][j]) 111 | assert(expt_str_1[i][j] == record_rows.get_field(j)) else pass = FAIL; 112 | end 113 | `MJOLNIR_NEW_TEST_END(test_parse_run_twice) 114 | endclass 115 | 116 | -------------------------------------------------------------------------------- /unittest/cases/ut_title_reader.sv: -------------------------------------------------------------------------------- 1 | class ut_title_reader extends mjolnir_testcase; 2 | csv_title_reader title_reader; 3 | 4 | function new(string the_name = ""); 5 | super.new(the_name); 6 | endfunction 7 | 8 | 9 | function void setup(); 10 | title_reader = new(); 11 | endfunction 12 | 13 | 14 | `MJOLNIR_NEW_TEST_BEGIN(test_titles, ut_title_reader) 15 | csv_table_with_title the_table; 16 | string expt_str[3] = '{"title1", "title2", "title 3"}; 17 | pass = PASS; 18 | 19 | the_table = self.title_reader.read_n_parse_csv("../cases/test_title_reader_0.csv"); 20 | assert(the_table != null) else pass = FAIL; 21 | foreach(expt_str[i]) 22 | assert(expt_str[i] == the_table.titles[i]) else begin 23 | pass = FAIL; 24 | $display("Exptect: |%s|, Observe: |%s|", expt_str[i], the_table.titles[i]); 25 | end; 26 | `MJOLNIR_NEW_TEST_END(test_titles) 27 | 28 | `MJOLNIR_NEW_TEST_BEGIN(test_records_with_column_name, ut_title_reader) 29 | csv_table_with_title the_table; 30 | string expt_str[3][3] = '{ 31 | '{"11", "21", " 31"}, 32 | '{"12", "22", " 32"}, 33 | '{"13", "23", " 33"}}; 34 | int title_mapping[string]; 35 | string col_val, title_name; 36 | 37 | pass = PASS; 38 | title_mapping["title1"] = 0; 39 | title_mapping["title2"] = 1; 40 | title_mapping["title 3"] = 2; 41 | 42 | the_table = self.title_reader.read_n_parse_csv("../cases/test_title_reader_0.csv"); 43 | assert(the_table != null) else pass = FAIL; 44 | foreach(expt_str[i]) begin 45 | title_mapping.first(title_name); 46 | do begin 47 | assert(the_table.get_field(i, title_name, col_val)) else pass = FAIL; 48 | assert(expt_str[i][title_mapping[title_name]] == col_val) else begin 49 | pass = FAIL; 50 | $display("Exptect: |%s|, Observe: |%s|", 51 | expt_str[i][title_mapping[title_name]], col_val); 52 | end; 53 | end 54 | while(title_mapping.next(title_name)); 55 | 56 | end 57 | `MJOLNIR_NEW_TEST_END(test_records_with_column_name) 58 | 59 | `MJOLNIR_NEW_TEST_BEGIN(test_records_err_0, ut_title_reader) 60 | csv_table_with_title the_table; 61 | string expt_str[3][3] = '{ 62 | '{"11", "21", " 31"}, 63 | '{"12", "22", "unexisted in CSV file"}, 64 | '{"13", "23", " 33"}}; 65 | int title_mapping[string]; 66 | int err_mapping[string]; 67 | string col_val, title_name; 68 | string s; 69 | 70 | pass = PASS; 71 | title_mapping["title1"] = 0; 72 | title_mapping["title2"] = 1; 73 | title_mapping["title 3"] = 2; 74 | 75 | $sformat(s, "%0d-title 3", 1); 76 | err_mapping[s] = 1; 77 | 78 | the_table = self.title_reader.read_n_parse_csv("../cases/test_title_reader_err_0.csv"); 79 | assert(the_table != null) else pass = FAIL; 80 | foreach(expt_str[i]) begin 81 | title_mapping.first(title_name); 82 | do begin 83 | $sformat(s, "%0d-%s", i, title_name); 84 | if(!err_mapping.exists(s)) 85 | assert(the_table.get_field(i, title_name, col_val)) else pass = FAIL; 86 | else begin 87 | assert(!the_table.get_field(i, title_name, col_val)) else pass = FAIL; 88 | break; // To skip the following comparison 89 | end 90 | 91 | assert(expt_str[i][title_mapping[title_name]] == col_val) else begin 92 | pass = FAIL; 93 | $display("Exptect: |%s|, Observe: |%s|", 94 | expt_str[i][title_mapping[title_name]], col_val); 95 | end; 96 | end 97 | while(title_mapping.next(title_name)); 98 | 99 | end 100 | `MJOLNIR_NEW_TEST_END(test_records_err_0) 101 | endclass 102 | 103 | -------------------------------------------------------------------------------- /unittest/sim/clr.sh: -------------------------------------------------------------------------------- 1 | 2 | rm -rf ./DVEfiles/ 3 | rm -rf ./csrc/ 4 | rm -rf ./simv.daidir/ 5 | rm -rf ./simv.vdb/ 6 | rm -rf ./.restartSimSession.tcl.old 7 | rm -rf ./cmp.log 8 | rm -rf ./sim.log 9 | rm -rf ./simv* 10 | rm -rf ./ucli.key 11 | rm -rf ./inter.vpd 12 | rm -rf ./compiled_libs/uvm/AN.DB 13 | rm -rf ./compiled_libs/uvm/.vcs_lib_lock 14 | rm -rf ./compiled_libs/work/AN.DB 15 | rm -rf ./compiled_libs/work/.vcs_lib_lock 16 | rm vc_hdrs.h 17 | -------------------------------------------------------------------------------- /unittest/sim/cover_need.txt: -------------------------------------------------------------------------------- 1 | 87: `COVER(submit_field();) 2 | 88: `COVER(submit_row(c);) 3 | 94: `COVER(submit_field();) 4 | 96: `COVER(state = FIELD_BEGUN;) 5 | 100:`COVER(state = FIELD_BEGUN;) 6 | 111:`COVER(submit_char(c);) 7 | 115:`COVER(return CSV_EPARSE;) 8 | 119:`COVER(submit_char(c);) 9 | 121:`COVER(submit_field();) 10 | 125:`COVER(submit_field();) 11 | 129:`COVER(submit_char(c);) 12 | 132:`COVER(submit_char(c);) 13 | 136:`COVER(submit_char(c);) 14 | 145:`COVER(entry_pos -= spaces + 1;) // get rid of spaces and original quote 15 | 149:`COVER(entry_pos -= spaces + 1;) // get rid of spaces and original quote 16 | 154:`COVER(submit_char(c);) 17 | 160:`COVER(return CSV_EPARSE;) 18 | 163:`COVER(state = FIELD_BEGUN;) 19 | 167:`COVER(return CSV_EPARSE;) 20 | -------------------------------------------------------------------------------- /unittest/sim/flist: -------------------------------------------------------------------------------- 1 | 2 | +incdir+../../uvm_src+../../uvm_src/macros 3 | +incdir+../lib/ 4 | +incdir+../../ 5 | +incdir+../unit_test/ 6 | -timescale=1ns/1ns 7 | 8 | +define+CODE_COVERAGE_MODE 9 | 10 | // Unit test framework 11 | ../unit_test/unit_test.sv 12 | 13 | // General Toolkits 14 | ../../csv_parser.svh 15 | 16 | // Unit test cases 17 | ../cases/ut_basic_unquoted.sv 18 | ../cases/ut_basic_quoted.sv 19 | ../cases/ut_title_reader.sv 20 | ../cases/run_all_test.sv 21 | -------------------------------------------------------------------------------- /unittest/sim/run.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/sh 3 | ./vlogana_uvm.sh 4 | ./vlogana_src.sh 5 | ana_done=$? 6 | echo "Vlogan done: $ana_done" 7 | echo "==========================================" 8 | if [ $ana_done -eq 0 ] 9 | then 10 | vcs -sverilog TB ../../uvm_src/dpi/uvm_dpi.cc -CFLAGS -DVCS -debug_all -l cmp.log 11 | fi 12 | 13 | if [ $? -eq 0 ] 14 | then 15 | ./simv -l sim.log 16 | fi 17 | -------------------------------------------------------------------------------- /unittest/sim/synopsys_sim.setup: -------------------------------------------------------------------------------- 1 | 2 | WORK > DEFAULT 3 | DEFAULT : ./compiled_libs/work 4 | UVM: ./compiled_libs/uvm 5 | TB: ./compiled_libs/tb 6 | -------------------------------------------------------------------------------- /unittest/sim/vlogana_src.sh: -------------------------------------------------------------------------------- 1 | 2 | vlogan -sverilog -f flist -work WORK 3 | -------------------------------------------------------------------------------- /unittest/sim/vlogana_uvm.sh: -------------------------------------------------------------------------------- 1 | 2 | vlogan -sverilog ../../uvm_src/uvm.sv +incdir+../../uvm_src+../../uvm_src/macros +define+UVM_OBJECT_MUST_HAVE_CONSTRUCTOR -work UVM 3 | -------------------------------------------------------------------------------- /unittest/unit_test/lib/mjolnir_test.sv: -------------------------------------------------------------------------------- 1 | // Author: Mo Chen 2 | // Apr 18, 2015 3 | 4 | `include "unit_test.svh" 5 | 6 | virtual class mjolnir_test `THE_BASE_CLASS_OF_MJOLNIR_UNITTEST ; 7 | pure virtual function void setup(); 8 | pure virtual function void tear_down(); 9 | pure virtual task run(ref TEST_RUN_STATUS pass); 10 | pure virtual function string test_name(); 11 | pure virtual function string test_status(); 12 | endclass 13 | 14 | 15 | class mjolnir_statistic extends mjolnir_test; 16 | TEST_RUN_STATUS test_passes[string]; 17 | int test_counter = 0; 18 | 19 | // Virtual functions & tasks inherited from its base 20 | virtual function void setup(); 21 | endfunction 22 | 23 | virtual function void tear_down(); 24 | endfunction 25 | 26 | virtual task run(ref TEST_RUN_STATUS pass); 27 | endtask 28 | 29 | virtual function string test_name(); 30 | return ""; 31 | endfunction 32 | 33 | virtual function string test_status(); 34 | endfunction 35 | ///////////////////////////////////////////////////// 36 | 37 | protected function TEST_RUN_STATUS test_conclusion(); 38 | TEST_RUN_STATUS pass = PASS; 39 | string test_name; 40 | 41 | test_passes.first(test_name); 42 | do begin 43 | pass = (!test_passes[test_name])?FAIL:pass; 44 | end 45 | while(test_passes.next(test_name)); 46 | return pass; 47 | endfunction 48 | 49 | protected function string format_test_by_name(string prefix = "test", string test_name); 50 | string s_rslt; 51 | $sformat(s_rslt, "\n%s-%s:\t\t%s", prefix, test_name, 52 | (test_passes[test_name] == PASS)?"PASS":"FAIL"); 53 | return s_rslt; 54 | endfunction 55 | 56 | protected function string format_statistic(string prefix = "test"); 57 | string s_counter; 58 | string s_test_rslts[$]; 59 | string s_statistic; 60 | string test_name; 61 | 62 | //$sformat(s_counter, "\nTotally, %0d %s have run", test_counter, prefix); 63 | 64 | //s_statistic = s_counter; 65 | s_statistic = ""; 66 | test_passes.first(test_name); 67 | do begin 68 | s_test_rslts.push_back(format_test_by_name(prefix, test_name)); 69 | end 70 | while(test_passes.next(test_name)); 71 | 72 | foreach(s_test_rslts[i]) begin 73 | s_statistic = {s_statistic, s_test_rslts[i]}; 74 | end 75 | return s_statistic; 76 | endfunction 77 | 78 | endclass 79 | 80 | 81 | virtual class mjolnir_task; 82 | pure virtual function string task_name(); 83 | pure virtual task test_task(ref TEST_RUN_STATUS pass); 84 | endclass 85 | 86 | 87 | class mjolnir_runner `THE_BASE_CLASS_OF_MJOLNIR_UNITTEST ; 88 | static mjolnir_test all_tests[$]; 89 | 90 | task run(); 91 | endtask 92 | endclass 93 | -------------------------------------------------------------------------------- /unittest/unit_test/lib/mjolnir_testcase.sv: -------------------------------------------------------------------------------- 1 | // Author: Mo Chen 2 | // Apr 18, 2015 3 | 4 | `include "unit_test.svh" 5 | 6 | class mjolnir_testcase extends mjolnir_statistic; 7 | mjolnir_task all_tasks[$]; 8 | string testcase_name = "Unnamed Testcase"; 9 | 10 | 11 | // Virtual functions & tasks inherited from its base 12 | virtual function void setup(); 13 | endfunction 14 | 15 | virtual function void tear_down(); 16 | endfunction 17 | 18 | virtual function string test_name(); 19 | return testcase_name; 20 | endfunction 21 | 22 | virtual task run(ref TEST_RUN_STATUS pass); 23 | string s; 24 | string s_statistic; 25 | 26 | test_counter = 0; 27 | foreach(all_tasks[i]) begin 28 | TEST_RUN_STATUS task_pass; 29 | $sformat(s, "\n----Running task: %s", all_tasks[i].task_name()); 30 | `MJOLNIR_INFO(s); 31 | 32 | setup(); 33 | all_tasks[i].test_task(task_pass); 34 | test_passes[all_tasks[i].task_name()] = task_pass; 35 | tear_down(); 36 | 37 | test_counter ++; 38 | end 39 | pass = test_conclusion(); 40 | 41 | s_statistic = format_statistic(test_name()); 42 | `MJOLNIR_INFO(s_statistic); 43 | endtask 44 | 45 | virtual function string test_status(); 46 | string s_statistic; 47 | string prefix ; 48 | $sformat(prefix, "----%s", test_name()); 49 | 50 | s_statistic = format_statistic(prefix); 51 | return s_statistic; 52 | endfunction 53 | ///////////////////////////////////////////////////// 54 | 55 | function new(string testcase_name=""); 56 | this.testcase_name = (testcase_name == "")?this.testcase_name:testcase_name; 57 | endfunction 58 | endclass 59 | 60 | -------------------------------------------------------------------------------- /unittest/unit_test/lib/mjolnir_testsuit.sv: -------------------------------------------------------------------------------- 1 | // Author: Mo Chen 2 | // Apr 18, 2015 3 | 4 | `include "unit_test.svh" 5 | 6 | class mjolnir_testsuit extends mjolnir_statistic; 7 | mjolnir_test all_tests[$]; 8 | string testsuit_name = "Unnamed Testsuit"; 9 | 10 | // Virtual functions & tasks inherited from its base 11 | virtual function void setup(); 12 | endfunction 13 | 14 | virtual function void tear_down(); 15 | endfunction 16 | 17 | virtual function string test_name(); 18 | return testsuit_name; 19 | endfunction 20 | 21 | virtual task run(ref TEST_RUN_STATUS pass); 22 | TEST_RUN_STATUS test_pass; 23 | string s; 24 | string s_statistic; 25 | 26 | $sformat(s, "\nRunning test suit: %s", test_name()); 27 | `MJOLNIR_INFO(s); 28 | 29 | test_counter = 0; 30 | foreach(all_tests[i]) begin 31 | $sformat(s, "\n--Running test: %s", all_tests[i].test_name()); 32 | `MJOLNIR_INFO(s); 33 | 34 | all_tests[i].run(test_pass); 35 | test_passes[all_tests[i].test_name()] = test_pass; 36 | 37 | test_counter ++; 38 | end 39 | pass = test_conclusion(); 40 | 41 | s_statistic = format_statistic(test_name()); 42 | `MJOLNIR_INFO(s_statistic); 43 | endtask 44 | 45 | virtual function string test_status(); 46 | string s_all_status[$]; 47 | string s_status; 48 | 49 | foreach(all_tests[i]) begin 50 | string sub_test_name = all_tests[i].test_name(); 51 | string prefix ; 52 | $sformat(prefix, "--%s", test_name()); 53 | s_status = {s_status, "\n"}; 54 | s_status = {s_status, format_test_by_name(prefix, sub_test_name)}; 55 | s_status = {s_status, all_tests[i].test_status()}; 56 | end 57 | 58 | return s_status; 59 | endfunction 60 | ///////////////////////////////////////////////////// 61 | 62 | function void add_test(mjolnir_test new_test); 63 | all_tests.push_back(new_test); 64 | endfunction 65 | 66 | function new(string testsuit_name=""); 67 | this.testsuit_name = (testsuit_name == "")?this.testsuit_name:testsuit_name; 68 | endfunction 69 | 70 | endclass 71 | 72 | -------------------------------------------------------------------------------- /unittest/unit_test/unit_test.sv: -------------------------------------------------------------------------------- 1 | `ifndef MJOLNIR_UNITTEST_TK_PREFIX 2 | `define MJOLNIR_UNITTEST_TK_PREFIX . 3 | `endif 4 | 5 | `define AUTO_BUILD_INC_PATH(T, ORIGINAL_PATH) `"``T``/``ORIGINAL_PATH```" 6 | 7 | package mjolnir_unit_test; 8 | 9 | typedef enum {PASS=1, FAIL=0} TEST_RUN_STATUS; 10 | 11 | `include `AUTO_BUILD_INC_PATH(`MJOLNIR_UNITTEST_TK_PREFIX, lib/mjolnir_test.sv) 12 | `include `AUTO_BUILD_INC_PATH(`MJOLNIR_UNITTEST_TK_PREFIX, lib/mjolnir_testcase.sv) 13 | `include `AUTO_BUILD_INC_PATH(`MJOLNIR_UNITTEST_TK_PREFIX, lib/mjolnir_testsuit.sv) 14 | 15 | endpackage 16 | 17 | `undef AUTO_BUILD_INC_PATH 18 | -------------------------------------------------------------------------------- /unittest/unit_test/unit_test.svh: -------------------------------------------------------------------------------- 1 | `ifndef SV_CODE_HEADER_UNIT_TEST 2 | `define SV_CODE_HEADER_UNIT_TEST 3 | 4 | //`define MJOLNIR_BASE_CLASS uvm_test 5 | `define MJOLNIR_INFO $display 6 | `define MJOLNIR_DEBUG $display 7 | `define MJOLNIR_FATAL $display 8 | `define MJOLNIR_ERROR $display 9 | 10 | `define BUILD_THE_BASE_CLASS_OF_UNIT_TEST(T) extends`` ``T 11 | 12 | `ifdef MJOLNIR_BASE_CLASS 13 | `define THE_BASE_CLASS_OF_MJOLNIR_UNITTEST `BUILD_THE_BASE_CLASS_OF_UNIT_TEST(`MJOLNIR_BASE_CLASS) 14 | `else 15 | `define THE_BASE_CLASS_OF_MJOLNIR_UNITTEST 16 | `endif 17 | 18 | `define MJOLNIR_NEW_TEST_BEGIN(NAME, TC_ENV) class NAME extends mjolnir_task; \ 19 | TC_ENV self; \ 20 | virtual function string task_name(); \ 21 | return "``NAME``"; \ 22 | endfunction \ 23 | function new(mjolnir_testcase its_belonging); \ 24 | its_belonging.all_tasks.push_back(this); \ 25 | $cast(self, its_belonging); \ 26 | endfunction \ 27 | virtual task test_task(ref mjolnir_unit_test::TEST_RUN_STATUS pass); \ 28 | pass = FAIL; \ 29 | begin 30 | 31 | `define MJOLNIR_NEW_TEST_END(NAME) end \ 32 | endtask \ 33 | endclass \ 34 | NAME _``NAME``_internal_obj = new(this); 35 | 36 | 37 | 38 | `endif 39 | --------------------------------------------------------------------------------