From 7764d0ba1dcf064ae487ee985c43083a0909e7f4 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 5 Jan 2013 11:13:26 +0100 Subject: [PATCH 1/1] initial import --- Makefile | 76 + README | 97 ++ backends/autotest/Makefile.inc | 3 + backends/autotest/autotest.cc | 309 ++++ backends/ilang/Makefile.inc | 3 + backends/ilang/ilang_backend.cc | 306 ++++ backends/ilang/ilang_backend.h | 47 + backends/verilog/Makefile.inc | 3 + backends/verilog/verilog_backend.cc | 905 +++++++++++ backends/verilog/verilog_backend.h | 39 + bigint/.gitignore | 6 + bigint/BigInteger.cc | 405 +++++ bigint/BigInteger.hh | 215 +++ bigint/BigIntegerAlgorithms.cc | 70 + bigint/BigIntegerAlgorithms.hh | 25 + bigint/BigIntegerLibrary.hh | 8 + bigint/BigIntegerUtils.cc | 50 + bigint/BigIntegerUtils.hh | 72 + bigint/BigUnsigned.cc | 697 +++++++++ bigint/BigUnsigned.hh | 418 +++++ bigint/BigUnsignedInABase.cc | 125 ++ bigint/BigUnsignedInABase.hh | 122 ++ bigint/ChangeLog | 146 ++ bigint/Makefile | 73 + bigint/NumberlikeArray.hh | 177 +++ bigint/README | 81 + bigint/run-testsuite | 37 + bigint/sample.cc | 125 ++ bigint/testsuite.cc | 326 ++++ frontends/ast/Makefile.inc | 5 + frontends/ast/ast.cc | 859 ++++++++++ frontends/ast/ast.h | 228 +++ frontends/ast/genrtlil.cc | 1054 +++++++++++++ frontends/ast/simplify.cc | 1081 +++++++++++++ frontends/ilang/Makefile.inc | 16 + frontends/ilang/ilang_frontend.cc | 49 + frontends/ilang/ilang_frontend.h | 45 + frontends/ilang/lexer.l | 122 ++ frontends/ilang/parser.y | 416 +++++ frontends/verilog/Makefile.inc | 19 + frontends/verilog/const2ast.cc | 197 +++ frontends/verilog/lexer.l | 264 ++++ frontends/verilog/parser.y | 1074 +++++++++++++ frontends/verilog/preproc.cc | 360 +++++ frontends/verilog/verilog_frontend.cc | 148 ++ frontends/verilog/verilog_frontend.h | 62 + kernel/bitpattern.h | 143 ++ kernel/calc.cc | 392 +++++ kernel/celltypes.h | 210 +++ kernel/consteval.h | 198 +++ kernel/driver.cc | 253 +++ kernel/log.cc | 197 +++ kernel/log.h | 51 + kernel/register.cc | 288 ++++ kernel/register.h | 80 + kernel/rtlil.cc | 1081 +++++++++++++ kernel/rtlil.h | 341 ++++ kernel/select.cc | 476 ++++++ kernel/sha1.cpp | 185 +++ kernel/sha1.h | 49 + kernel/show.cc | 343 ++++ kernel/sigtools.h | 415 +++++ passes/abc/Makefile.inc | 4 + passes/abc/abc.cc | 645 ++++++++ passes/abc/vlparse.cc | 198 +++ passes/abc/vlparse.h | 28 + passes/dfflibmap/Makefile.inc | 10 + passes/dfflibmap/dfflibmap.cc | 326 ++++ passes/dfflibmap/filterlib.cc | 4 + passes/dfflibmap/libparse.cc | 629 ++++++++ passes/dfflibmap/libparse.h | 56 + passes/fsm/Makefile.inc | 11 + passes/fsm/fsm.cc | 91 ++ passes/fsm/fsm_detect.cc | 160 ++ passes/fsm/fsm_expand.cc | 255 +++ passes/fsm/fsm_export.cc | 103 ++ passes/fsm/fsm_extract.cc | 359 +++++ passes/fsm/fsm_info.cc | 46 + passes/fsm/fsm_map.cc | 356 +++++ passes/fsm/fsm_opt.cc | 285 ++++ passes/fsm/fsm_recode.cc | 114 ++ passes/fsm/fsmdata.h | 177 +++ passes/hierarchy/Makefile.inc | 3 + passes/hierarchy/hierarchy.cc | 194 +++ passes/memory/Makefile.inc | 6 + passes/memory/memory.cc | 40 + passes/memory/memory_collect.cc | 182 +++ passes/memory/memory_dff.cc | 200 +++ passes/memory/memory_map.cc | 334 ++++ passes/opt/Makefile.inc | 9 + passes/opt/opt.cc | 62 + passes/opt/opt_const.cc | 263 ++++ passes/opt/opt_muxtree.cc | 417 +++++ passes/opt/opt_reduce.cc | 236 +++ passes/opt/opt_rmdff.cc | 135 ++ passes/opt/opt_rmunused.cc | 239 +++ passes/opt/opt_share.cc | 250 +++ passes/opt/opt_status.h | 26 + passes/proc/Makefile.inc | 8 + passes/proc/proc.cc | 44 + passes/proc/proc_arst.cc | 191 +++ passes/proc/proc_clean.cc | 160 ++ passes/proc/proc_dff.cc | 178 +++ passes/proc/proc_mux.cc | 294 ++++ passes/proc/proc_rmdead.cc | 87 ++ passes/submod/Makefile.inc | 3 + passes/submod/submod.cc | 273 ++++ passes/techmap/Makefile.inc | 11 + passes/techmap/techmap.cc | 207 +++ techlibs/Makefile.inc | 7 + techlibs/blackbox.sed | 4 + techlibs/simlib.v | 892 +++++++++++ techlibs/stdcells.v | 1378 +++++++++++++++++ techlibs/stdcells_sim.v | 166 ++ tests/asicworld/README | 1 + tests/asicworld/code_hdl_models_GrayCounter.v | 33 + tests/asicworld/code_hdl_models_arbiter.v | 123 ++ tests/asicworld/code_hdl_models_arbiter_tb.v | 62 + tests/asicworld/code_hdl_models_cam.v | 60 + tests/asicworld/code_hdl_models_clk_div.v | 27 + tests/asicworld/code_hdl_models_clk_div_45.v | 54 + tests/asicworld/code_hdl_models_d_ff_gates.v | 29 + .../asicworld/code_hdl_models_d_latch_gates.v | 15 + .../code_hdl_models_decoder_2to4_gates.v | 14 + .../code_hdl_models_decoder_using_assign.v | 20 + .../code_hdl_models_decoder_using_case.v | 43 + .../code_hdl_models_dff_async_reset.v | 30 + .../code_hdl_models_dff_sync_reset.v | 30 + .../asicworld/code_hdl_models_dlatch_reset.v | 30 + .../code_hdl_models_encoder_4to2_gates.v | 8 + .../code_hdl_models_encoder_using_case.v | 42 + .../code_hdl_models_encoder_using_if.v | 58 + .../code_hdl_models_full_adder_gates.v | 18 + .../code_hdl_models_full_subtracter_gates.v | 20 + .../asicworld/code_hdl_models_gray_counter.v | 33 + .../code_hdl_models_half_adder_gates.v | 14 + tests/asicworld/code_hdl_models_lfsr.v | 35 + tests/asicworld/code_hdl_models_lfsr_updown.v | 35 + tests/asicworld/code_hdl_models_misc1.v | 22 + .../asicworld/code_hdl_models_mux21_switch.v | 22 + .../code_hdl_models_mux_2to1_gates.v | 18 + .../code_hdl_models_mux_using_assign.v | 22 + .../code_hdl_models_mux_using_case.v | 28 + .../asicworld/code_hdl_models_mux_using_if.v | 29 + tests/asicworld/code_hdl_models_nand_switch.v | 14 + tests/asicworld/code_hdl_models_one_hot_cnt.v | 31 + .../asicworld/code_hdl_models_parallel_crc.v | 53 + .../code_hdl_models_parity_using_assign.v | 21 + .../code_hdl_models_parity_using_bitwise.v | 16 + .../code_hdl_models_parity_using_function.v | 29 + ...code_hdl_models_pri_encoder_using_assign.v | 36 + .../asicworld/code_hdl_models_ram_sp_ar_sw.v | 58 + .../asicworld/code_hdl_models_ram_sp_sr_sw.v | 62 + .../code_hdl_models_rom_using_case.v | 42 + tests/asicworld/code_hdl_models_serial_crc.v | 54 + .../asicworld/code_hdl_models_t_gate_switch.v | 11 + .../code_hdl_models_tff_async_reset.v | 27 + .../code_hdl_models_tff_sync_reset.v | 27 + tests/asicworld/code_hdl_models_uart.v | 154 ++ tests/asicworld/code_hdl_models_up_counter.v | 29 + .../code_hdl_models_up_counter_load.v | 32 + .../code_hdl_models_up_down_counter.v | 29 + tests/asicworld/code_specman_switch_fabric.v | 82 + tests/asicworld/code_tidbits_asyn_reset.v | 18 + tests/asicworld/code_tidbits_blocking.v | 17 + .../asicworld/code_tidbits_fsm_using_always.v | 91 ++ .../code_tidbits_fsm_using_function.v | 94 ++ .../code_tidbits_fsm_using_single_always.v | 63 + tests/asicworld/code_tidbits_nonblocking.v | 17 + .../code_tidbits_reg_combo_example.v | 13 + .../asicworld/code_tidbits_reg_seq_example.v | 15 + tests/asicworld/code_tidbits_syn_reset.v | 19 + tests/asicworld/code_tidbits_wire_example.v | 9 + .../asicworld/code_verilog_tutorial_addbit.v | 24 + .../code_verilog_tutorial_always_example.v | 11 + .../asicworld/code_verilog_tutorial_bus_con.v | 8 + .../asicworld/code_verilog_tutorial_comment.v | 25 + .../asicworld/code_verilog_tutorial_counter.v | 19 + .../code_verilog_tutorial_counter_tb.v | 113 ++ tests/asicworld/code_verilog_tutorial_d_ff.v | 14 + .../asicworld/code_verilog_tutorial_decoder.v | 14 + .../code_verilog_tutorial_decoder_always.v | 20 + .../code_verilog_tutorial_escape_id.v | 14 + .../code_verilog_tutorial_explicit.v | 35 + .../code_verilog_tutorial_first_counter.v | 47 + .../code_verilog_tutorial_first_counter_tb.v | 36 + .../code_verilog_tutorial_flip_flop.v | 15 + .../code_verilog_tutorial_fsm_full.v | 114 ++ .../code_verilog_tutorial_fsm_full_tb.v | 48 + .../code_verilog_tutorial_good_code.v | 18 + .../asicworld/code_verilog_tutorial_if_else.v | 13 + .../code_verilog_tutorial_multiply.v | 8 + .../asicworld/code_verilog_tutorial_mux_21.v | 9 + .../code_verilog_tutorial_n_out_primitive.v | 13 + .../code_verilog_tutorial_parallel_if.v | 21 + .../asicworld/code_verilog_tutorial_parity.v | 41 + .../code_verilog_tutorial_simple_function.v | 10 + .../code_verilog_tutorial_simple_if.v | 11 + .../code_verilog_tutorial_task_global.v | 12 + .../asicworld/code_verilog_tutorial_tri_buf.v | 9 + .../asicworld/code_verilog_tutorial_v2k_reg.v | 24 + .../code_verilog_tutorial_which_clock.v | 12 + tests/asicworld/run-test.sh | 3 + tests/hana/README | 14 + tests/hana/hana_vlib.v | 1139 ++++++++++++++ tests/hana/run-test.sh | 3 + .../hana/test_intermout_always_comb_1_test.v | 13 + .../hana/test_intermout_always_comb_3_test.v | 10 + .../hana/test_intermout_always_comb_4_test.v | 9 + .../hana/test_intermout_always_comb_5_test.v | 11 + tests/hana/test_intermout_always_ff_3_test.v | 15 + tests/hana/test_intermout_always_ff_4_test.v | 11 + tests/hana/test_intermout_always_ff_5_test.v | 13 + tests/hana/test_intermout_always_ff_6_test.v | 7 + tests/hana/test_intermout_always_ff_8_test.v | 11 + tests/hana/test_intermout_always_ff_9_test.v | 14 + .../hana/test_intermout_always_latch_1_test.v | 9 + tests/hana/test_intermout_bufrm_1_test.v | 4 + tests/hana/test_intermout_bufrm_2_test.v | 7 + tests/hana/test_intermout_bufrm_6_test.v | 22 + tests/hana/test_intermout_bufrm_7_test.v | 33 + tests/hana/test_intermout_exprs_add_test.v | 10 + .../hana/test_intermout_exprs_binlogic_test.v | 13 + .../test_intermout_exprs_bitwiseneg_test.v | 5 + tests/hana/test_intermout_exprs_buffer_test.v | 9 + .../test_intermout_exprs_condexpr_mux_test.v | 11 + ...est_intermout_exprs_condexpr_tribuf_test.v | 9 + tests/hana/test_intermout_exprs_const_test.v | 7 + .../test_intermout_exprs_constshift_test.v | 12 + tests/hana/test_intermout_exprs_div_test.v | 10 + .../hana/test_intermout_exprs_logicneg_test.v | 7 + tests/hana/test_intermout_exprs_mod_test.v | 10 + tests/hana/test_intermout_exprs_mul_test.v | 10 + tests/hana/test_intermout_exprs_redand_test.v | 5 + tests/hana/test_intermout_exprs_redop_test.v | 16 + tests/hana/test_intermout_exprs_sub_test.v | 10 + .../test_intermout_exprs_unaryminus_test.v | 5 + .../test_intermout_exprs_unaryplus_test.v | 4 + .../hana/test_intermout_exprs_varshift_test.v | 10 + .../test_parse2synthtrans_behavopt_1_test.v | 22 + .../hana/test_parse2synthtrans_case_1_test.v | 26 + .../test_parse2synthtrans_contassign_1_test.v | 7 + ...test_parse2synthtrans_module_basic0_test.v | 2 + .../test_parse2synthtrans_operators_1_test.v | 11 + .../hana/test_parse2synthtrans_param_1_test.v | 7 + ...test_parse2synthtrans_port_scalar_1_test.v | 6 + ...test_parse2synthtrans_port_vector_1_test.v | 9 + ...synthtrans_v2k_comb_logic_sens_list_test.v | 9 + ...est_parser_constructs_module_basic1_test.v | 2 + ...test_parser_constructs_param_basic0_test.v | 10 + .../test_parser_constructs_port_basic0_test.v | 8 + ..._parser_directives_define_simpledef_test.v | 9 + tests/hana/test_parser_misc_operators_test.v | 29 + ...test_parser_v2k_comb_port_data_type_test.v | 6 + ...test_parser_v2k_comma_sep_sens_list_test.v | 9 + tests/hana/test_simulation_always_15_test.v | 5 + tests/hana/test_simulation_always_17_test.v | 13 + tests/hana/test_simulation_always_18_test.v | 10 + tests/hana/test_simulation_always_19_test.v | 11 + tests/hana/test_simulation_always_1_test.v | 5 + tests/hana/test_simulation_always_20_test.v | 15 + tests/hana/test_simulation_always_21_test.v | 11 + tests/hana/test_simulation_always_22_test.v | 7 + tests/hana/test_simulation_always_23_test.v | 14 + tests/hana/test_simulation_always_27_test.v | 13 + tests/hana/test_simulation_always_29_test.v | 9 + tests/hana/test_simulation_always_31_tt.v | 50 + tests/hana/test_simulation_and_1_test.v | 3 + tests/hana/test_simulation_and_2_test.v | 3 + tests/hana/test_simulation_and_3_test.v | 3 + tests/hana/test_simulation_and_4_test.v | 3 + tests/hana/test_simulation_and_5_test.v | 3 + tests/hana/test_simulation_and_6_test.v | 3 + tests/hana/test_simulation_and_7_test.v | 3 + tests/hana/test_simulation_buffer_1_test.v | 3 + tests/hana/test_simulation_buffer_2_test.v | 4 + tests/hana/test_simulation_buffer_3_test.v | 4 + tests/hana/test_simulation_decoder_2_test.v | 14 + tests/hana/test_simulation_decoder_3_test.v | 14 + tests/hana/test_simulation_decoder_4_test.v | 14 + tests/hana/test_simulation_decoder_5_test.v | 17 + tests/hana/test_simulation_decoder_6_test.v | 27 + tests/hana/test_simulation_decoder_7_test.v | 43 + tests/hana/test_simulation_decoder_8_test.v | 76 + tests/hana/test_simulation_inc_16_test.v | 5 + tests/hana/test_simulation_inc_1_test.v | 5 + tests/hana/test_simulation_inc_2_test.v | 5 + tests/hana/test_simulation_inc_32_test.v | 5 + tests/hana/test_simulation_inc_4_test.v | 5 + tests/hana/test_simulation_inc_8_test.v | 5 + tests/hana/test_simulation_mod_1_xx.v | 13 + tests/hana/test_simulation_mux_16_test.v | 22 + tests/hana/test_simulation_mux_2_test.v | 8 + tests/hana/test_simulation_mux_32_test.v | 39 + tests/hana/test_simulation_mux_4_test.v | 10 + tests/hana/test_simulation_mux_64_test.v | 71 + tests/hana/test_simulation_mux_8_test.v | 14 + tests/hana/test_simulation_nand_1_test.v | 3 + tests/hana/test_simulation_nand_3_test.v | 3 + tests/hana/test_simulation_nand_4_test.v | 3 + tests/hana/test_simulation_nand_5_test.v | 3 + tests/hana/test_simulation_nand_6_test.v | 3 + tests/hana/test_simulation_nor_1_test.v | 3 + tests/hana/test_simulation_nor_2_test.v | 3 + tests/hana/test_simulation_nor_3_test.v | 3 + tests/hana/test_simulation_nor_4_test.v | 3 + ...mulation_opt_constprop_contassign_1_test.v | 3 + tests/hana/test_simulation_or_1_test.v | 3 + tests/hana/test_simulation_or_2_test.v | 3 + tests/hana/test_simulation_or_3_test.v | 3 + tests/hana/test_simulation_or_4_test.v | 3 + tests/hana/test_simulation_or_5_test.v | 3 + tests/hana/test_simulation_or_6_test.v | 3 + tests/hana/test_simulation_seq_ff_1_test.v | 4 + tests/hana/test_simulation_seq_ff_2_test.v | 4 + .../test_simulation_shifter_left_16_test.v | 4 + .../test_simulation_shifter_left_32_test.v | 4 + .../test_simulation_shifter_left_4_test.v | 4 + .../test_simulation_shifter_left_64_test.v | 4 + .../test_simulation_shifter_left_8_test.v | 4 + .../test_simulation_shifter_right_16_test.v | 4 + .../test_simulation_shifter_right_32_test.v | 4 + .../test_simulation_shifter_right_4_test.v | 4 + .../test_simulation_shifter_right_64_test.v | 4 + .../test_simulation_shifter_right_8_test.v | 4 + .../hana/test_simulation_sop_basic_10_test.v | 8 + .../hana/test_simulation_sop_basic_11_test.v | 10 + .../hana/test_simulation_sop_basic_12_test.v | 14 + .../hana/test_simulation_sop_basic_18_test.v | 5 + tests/hana/test_simulation_sop_basic_3_test.v | 3 + tests/hana/test_simulation_sop_basic_7_test.v | 3 + tests/hana/test_simulation_sop_basic_8_test.v | 3 + tests/hana/test_simulation_sop_basic_9_test.v | 3 + .../test_simulation_techmap_and_19_tech.v | 7 + .../hana/test_simulation_techmap_and_5_tech.v | 3 + tests/hana/test_simulation_techmap_buf_test.v | 3 + tests/hana/test_simulation_techmap_inv_test.v | 3 + .../hana/test_simulation_techmap_mux_0_test.v | 8 + .../test_simulation_techmap_mux_128_test.v | 134 ++ .../hana/test_simulation_techmap_mux_8_test.v | 14 + .../test_simulation_techmap_nand_19_tech.v | 11 + .../test_simulation_techmap_nand_2_tech.v | 11 + .../test_simulation_techmap_nand_5_tech.v | 11 + .../test_simulation_techmap_nor_19_tech.v | 11 + .../hana/test_simulation_techmap_nor_2_tech.v | 11 + .../hana/test_simulation_techmap_nor_5_tech.v | 11 + .../hana/test_simulation_techmap_or_19_tech.v | 7 + .../hana/test_simulation_techmap_or_5_tech.v | 3 + .../test_simulation_techmap_xnor_2_tech.v | 6 + .../test_simulation_techmap_xnor_5_tech.v | 6 + .../test_simulation_techmap_xor_19_tech.v | 3 + .../hana/test_simulation_techmap_xor_2_tech.v | 6 + .../hana/test_simulation_techmap_xor_5_tech.v | 6 + tests/hana/test_simulation_tribuf_2_test.v | 3 + tests/hana/test_simulation_xnor_1_test.v | 3 + tests/hana/test_simulation_xnor_2_test.v | 3 + tests/hana/test_simulation_xnor_3_test.v | 3 + tests/hana/test_simulation_xnor_4_test.v | 3 + tests/hana/test_simulation_xor_1_test.v | 3 + tests/hana/test_simulation_xor_2_test.v | 3 + tests/hana/test_simulation_xor_3_test.v | 3 + tests/hana/test_simulation_xor_4_test.v | 3 + tests/i2c_bench/i2c_master_bit_ctrl.v | 576 +++++++ tests/i2c_bench/i2c_master_byte_ctrl.v | 344 ++++ tests/i2c_bench/i2c_master_defines.v | 59 + tests/i2c_bench/i2c_master_top.v | 301 ++++ tests/i2c_bench/i2c_slave_model.v | 361 +++++ tests/i2c_bench/run-test.sh | 50 + tests/i2c_bench/spi_slave_model.v | 125 ++ tests/i2c_bench/timescale.v | 2 + tests/i2c_bench/tst_bench_top.v | 468 ++++++ tests/i2c_bench/wb_master_model.v | 205 +++ tests/iwls2005/README | 7 + tests/iwls2005/aes_core/aes_cipher_top.v | 256 +++ tests/iwls2005/aes_core/aes_inv_cipher_top.v | 327 ++++ tests/iwls2005/aes_core/aes_inv_sbox.v | 328 ++++ tests/iwls2005/aes_core/aes_key_expand_128.v | 87 ++ tests/iwls2005/aes_core/aes_rcon.v | 96 ++ tests/iwls2005/aes_core/aes_sbox.v | 329 ++++ tests/iwls2005/aes_core/timescale.v | 1 + tests/iwls2005/fpu/except.v | 153 ++ tests/iwls2005/fpu/fpu.v | 560 +++++++ tests/iwls2005/fpu/post_norm.v | 676 ++++++++ tests/iwls2005/fpu/pre_norm.v | 270 ++++ tests/iwls2005/fpu/pre_norm_fmul.v | 150 ++ tests/iwls2005/fpu/primitives.v | 103 ++ tests/iwls2005/i2c/i2c_master_bit_ctrl.v | 535 +++++++ tests/iwls2005/i2c/i2c_master_byte_ctrl.v | 344 ++++ tests/iwls2005/i2c/i2c_master_defines.v | 64 + tests/iwls2005/i2c/i2c_master_top.v | 301 ++++ tests/iwls2005/i2c/timescale.v | 2 + tests/iwls2005/run-fm.sh | 42 + tests/iwls2005/run-synth.sh | 45 + tests/iwls2005/run-synth.ys | 11 + tests/iwls2005/sasc/sasc_brg.v | 160 ++ tests/iwls2005/sasc/sasc_fifo4.v | 135 ++ tests/iwls2005/sasc/sasc_top.v | 301 ++++ tests/iwls2005/sasc/timescale.v | 1 + tests/iwls2005/simple_spi/fifo4.v | 134 ++ tests/iwls2005/simple_spi/simple_spi_top.v | 329 ++++ tests/iwls2005/spi/spi_clgen.v | 108 ++ tests/iwls2005/spi/spi_defines.v | 159 ++ tests/iwls2005/spi/spi_shift.v | 238 +++ tests/iwls2005/spi/spi_top.v | 287 ++++ tests/iwls2005/spi/timescale.v | 2 + tests/iwls2005/ss_pcm/pcm_slv_top.v | 222 +++ tests/iwls2005/ss_pcm/timescale.v | 1 + tests/iwls2005/systemcaes/aes.v | 358 +++++ tests/iwls2005/systemcaes/byte_mixcolum.v | 92 ++ tests/iwls2005/systemcaes/keysched.v | 248 +++ tests/iwls2005/systemcaes/mixcolum.v | 188 +++ tests/iwls2005/systemcaes/sbox.v | 392 +++++ tests/iwls2005/systemcaes/subbytes.v | 259 ++++ tests/iwls2005/systemcaes/timescale.v | 1 + tests/iwls2005/systemcaes/word_mixcolum.v | 124 ++ tests/iwls2005/usb_phy/timescale.v | 1 + tests/iwls2005/usb_phy/usb_phy.v | 184 +++ tests/iwls2005/usb_phy/usb_rx_phy.v | 452 ++++++ tests/iwls2005/usb_phy/usb_tx_phy.v | 465 ++++++ tests/no-icarus/README | 2 + tests/no-icarus/autowire.v | 25 + tests/no-icarus/var_range.v | 45 + tests/openmsp430/rtl/omsp_alu.v | 258 +++ tests/openmsp430/rtl/omsp_and_gate.v | 89 ++ tests/openmsp430/rtl/omsp_clock_gate.v | 86 + tests/openmsp430/rtl/omsp_clock_module.v | 1058 +++++++++++++ tests/openmsp430/rtl/omsp_clock_mux.v | 192 +++ tests/openmsp430/rtl/omsp_dbg.v | 827 ++++++++++ tests/openmsp430/rtl/omsp_dbg_hwbrk.v | 282 ++++ tests/openmsp430/rtl/omsp_dbg_uart.v | 298 ++++ tests/openmsp430/rtl/omsp_execution_unit.v | 420 +++++ tests/openmsp430/rtl/omsp_frontend.v | 966 ++++++++++++ tests/openmsp430/rtl/omsp_mem_backbone.v | 275 ++++ tests/openmsp430/rtl/omsp_multiplier.v | 420 +++++ tests/openmsp430/rtl/omsp_register_file.v | 618 ++++++++ tests/openmsp430/rtl/omsp_scan_mux.v | 75 + tests/openmsp430/rtl/omsp_sfr.v | 353 +++++ tests/openmsp430/rtl/omsp_sync_cell.v | 80 + tests/openmsp430/rtl/omsp_sync_reset.v | 78 + tests/openmsp430/rtl/omsp_wakeup_cell.v | 108 ++ tests/openmsp430/rtl/omsp_watchdog.v | 556 +++++++ tests/openmsp430/rtl/openMSP430.v | 584 +++++++ tests/openmsp430/rtl/openMSP430_defines.v | 843 ++++++++++ tests/openmsp430/rtl/openMSP430_undefines.v | 732 +++++++++ tests/openmsp430/run-fm.do | 37 + tests/openmsp430/run-fm.sh | 5 + tests/openmsp430/run-synth.sh | 3 + tests/openmsp430/run-synth.ys | 11 + tests/openmsp430/sim_mul.v | 29 + tests/or1200/config.patch | 46 + tests/or1200/run-checkout.sh | 4 + tests/or1200/run-fm-mods.sh | 24 + tests/or1200/run-fm.do | 53 + tests/or1200/run-fm.sh | 5 + tests/or1200/run-synth.sh | 2 + tests/or1200/run-synth.ys | 11 + tests/or1200/run-vg.sh | 4 + tests/simple/aes_kexp128.v | 24 + tests/simple/dff_different_styles.v | 52 + tests/simple/fiedler-cooley.v | 33 + tests/simple/fsm.v | 69 + tests/simple/generate.v | 67 + tests/simple/i2c_master_tests.v | 62 + tests/simple/loops.v | 79 + tests/simple/mem2reg.v | 17 + tests/simple/memory.v | 19 + tests/simple/muxtree.v | 50 + tests/simple/omsp_dbg_uart.v | 34 + tests/simple/operators.v | 97 ++ tests/simple/paramods.v | 37 + tests/simple/process.v | 65 + tests/simple/run-test.sh | 3 + tests/simple/subbytes.v | 82 + tests/simple/task_func.v | 35 + tests/simple/usb_phy_tetsts.v | 36 + tests/simple/values.v | 44 + tests/tools/autotest.sh | 164 ++ tests/tools/cmp_tbdata.c | 67 + tests/tools/profiler.pl | 55 + tests/tools/rtlview.sh | 63 + tests/tools/vcdcd.pl | 201 +++ 481 files changed, 54634 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 backends/autotest/Makefile.inc create mode 100644 backends/autotest/autotest.cc create mode 100644 backends/ilang/Makefile.inc create mode 100644 backends/ilang/ilang_backend.cc create mode 100644 backends/ilang/ilang_backend.h create mode 100644 backends/verilog/Makefile.inc create mode 100644 backends/verilog/verilog_backend.cc create mode 100644 backends/verilog/verilog_backend.h create mode 100644 bigint/.gitignore create mode 100644 bigint/BigInteger.cc create mode 100644 bigint/BigInteger.hh create mode 100644 bigint/BigIntegerAlgorithms.cc create mode 100644 bigint/BigIntegerAlgorithms.hh create mode 100644 bigint/BigIntegerLibrary.hh create mode 100644 bigint/BigIntegerUtils.cc create mode 100644 bigint/BigIntegerUtils.hh create mode 100644 bigint/BigUnsigned.cc create mode 100644 bigint/BigUnsigned.hh create mode 100644 bigint/BigUnsignedInABase.cc create mode 100644 bigint/BigUnsignedInABase.hh create mode 100644 bigint/ChangeLog create mode 100644 bigint/Makefile create mode 100644 bigint/NumberlikeArray.hh create mode 100644 bigint/README create mode 100755 bigint/run-testsuite create mode 100644 bigint/sample.cc create mode 100644 bigint/testsuite.cc create mode 100644 frontends/ast/Makefile.inc create mode 100644 frontends/ast/ast.cc create mode 100644 frontends/ast/ast.h create mode 100644 frontends/ast/genrtlil.cc create mode 100644 frontends/ast/simplify.cc create mode 100644 frontends/ilang/Makefile.inc create mode 100644 frontends/ilang/ilang_frontend.cc create mode 100644 frontends/ilang/ilang_frontend.h create mode 100644 frontends/ilang/lexer.l create mode 100644 frontends/ilang/parser.y create mode 100644 frontends/verilog/Makefile.inc create mode 100644 frontends/verilog/const2ast.cc create mode 100644 frontends/verilog/lexer.l create mode 100644 frontends/verilog/parser.y create mode 100644 frontends/verilog/preproc.cc create mode 100644 frontends/verilog/verilog_frontend.cc create mode 100644 frontends/verilog/verilog_frontend.h create mode 100644 kernel/bitpattern.h create mode 100644 kernel/calc.cc create mode 100644 kernel/celltypes.h create mode 100644 kernel/consteval.h create mode 100644 kernel/driver.cc create mode 100644 kernel/log.cc create mode 100644 kernel/log.h create mode 100644 kernel/register.cc create mode 100644 kernel/register.h create mode 100644 kernel/rtlil.cc create mode 100644 kernel/rtlil.h create mode 100644 kernel/select.cc create mode 100644 kernel/sha1.cpp create mode 100644 kernel/sha1.h create mode 100644 kernel/show.cc create mode 100644 kernel/sigtools.h create mode 100644 passes/abc/Makefile.inc create mode 100644 passes/abc/abc.cc create mode 100644 passes/abc/vlparse.cc create mode 100644 passes/abc/vlparse.h create mode 100644 passes/dfflibmap/Makefile.inc create mode 100644 passes/dfflibmap/dfflibmap.cc create mode 100644 passes/dfflibmap/filterlib.cc create mode 100644 passes/dfflibmap/libparse.cc create mode 100644 passes/dfflibmap/libparse.h create mode 100644 passes/fsm/Makefile.inc create mode 100644 passes/fsm/fsm.cc create mode 100644 passes/fsm/fsm_detect.cc create mode 100644 passes/fsm/fsm_expand.cc create mode 100644 passes/fsm/fsm_export.cc create mode 100644 passes/fsm/fsm_extract.cc create mode 100644 passes/fsm/fsm_info.cc create mode 100644 passes/fsm/fsm_map.cc create mode 100644 passes/fsm/fsm_opt.cc create mode 100644 passes/fsm/fsm_recode.cc create mode 100644 passes/fsm/fsmdata.h create mode 100644 passes/hierarchy/Makefile.inc create mode 100644 passes/hierarchy/hierarchy.cc create mode 100644 passes/memory/Makefile.inc create mode 100644 passes/memory/memory.cc create mode 100644 passes/memory/memory_collect.cc create mode 100644 passes/memory/memory_dff.cc create mode 100644 passes/memory/memory_map.cc create mode 100644 passes/opt/Makefile.inc create mode 100644 passes/opt/opt.cc create mode 100644 passes/opt/opt_const.cc create mode 100644 passes/opt/opt_muxtree.cc create mode 100644 passes/opt/opt_reduce.cc create mode 100644 passes/opt/opt_rmdff.cc create mode 100644 passes/opt/opt_rmunused.cc create mode 100644 passes/opt/opt_share.cc create mode 100644 passes/opt/opt_status.h create mode 100644 passes/proc/Makefile.inc create mode 100644 passes/proc/proc.cc create mode 100644 passes/proc/proc_arst.cc create mode 100644 passes/proc/proc_clean.cc create mode 100644 passes/proc/proc_dff.cc create mode 100644 passes/proc/proc_mux.cc create mode 100644 passes/proc/proc_rmdead.cc create mode 100644 passes/submod/Makefile.inc create mode 100644 passes/submod/submod.cc create mode 100644 passes/techmap/Makefile.inc create mode 100644 passes/techmap/techmap.cc create mode 100644 techlibs/Makefile.inc create mode 100644 techlibs/blackbox.sed create mode 100644 techlibs/simlib.v create mode 100644 techlibs/stdcells.v create mode 100644 techlibs/stdcells_sim.v create mode 100644 tests/asicworld/README create mode 100644 tests/asicworld/code_hdl_models_GrayCounter.v create mode 100644 tests/asicworld/code_hdl_models_arbiter.v create mode 100644 tests/asicworld/code_hdl_models_arbiter_tb.v create mode 100644 tests/asicworld/code_hdl_models_cam.v create mode 100644 tests/asicworld/code_hdl_models_clk_div.v create mode 100644 tests/asicworld/code_hdl_models_clk_div_45.v create mode 100644 tests/asicworld/code_hdl_models_d_ff_gates.v create mode 100644 tests/asicworld/code_hdl_models_d_latch_gates.v create mode 100644 tests/asicworld/code_hdl_models_decoder_2to4_gates.v create mode 100644 tests/asicworld/code_hdl_models_decoder_using_assign.v create mode 100644 tests/asicworld/code_hdl_models_decoder_using_case.v create mode 100644 tests/asicworld/code_hdl_models_dff_async_reset.v create mode 100644 tests/asicworld/code_hdl_models_dff_sync_reset.v create mode 100644 tests/asicworld/code_hdl_models_dlatch_reset.v create mode 100644 tests/asicworld/code_hdl_models_encoder_4to2_gates.v create mode 100644 tests/asicworld/code_hdl_models_encoder_using_case.v create mode 100644 tests/asicworld/code_hdl_models_encoder_using_if.v create mode 100644 tests/asicworld/code_hdl_models_full_adder_gates.v create mode 100644 tests/asicworld/code_hdl_models_full_subtracter_gates.v create mode 100644 tests/asicworld/code_hdl_models_gray_counter.v create mode 100644 tests/asicworld/code_hdl_models_half_adder_gates.v create mode 100644 tests/asicworld/code_hdl_models_lfsr.v create mode 100644 tests/asicworld/code_hdl_models_lfsr_updown.v create mode 100644 tests/asicworld/code_hdl_models_misc1.v create mode 100644 tests/asicworld/code_hdl_models_mux21_switch.v create mode 100644 tests/asicworld/code_hdl_models_mux_2to1_gates.v create mode 100644 tests/asicworld/code_hdl_models_mux_using_assign.v create mode 100644 tests/asicworld/code_hdl_models_mux_using_case.v create mode 100644 tests/asicworld/code_hdl_models_mux_using_if.v create mode 100644 tests/asicworld/code_hdl_models_nand_switch.v create mode 100644 tests/asicworld/code_hdl_models_one_hot_cnt.v create mode 100644 tests/asicworld/code_hdl_models_parallel_crc.v create mode 100644 tests/asicworld/code_hdl_models_parity_using_assign.v create mode 100644 tests/asicworld/code_hdl_models_parity_using_bitwise.v create mode 100644 tests/asicworld/code_hdl_models_parity_using_function.v create mode 100644 tests/asicworld/code_hdl_models_pri_encoder_using_assign.v create mode 100644 tests/asicworld/code_hdl_models_ram_sp_ar_sw.v create mode 100644 tests/asicworld/code_hdl_models_ram_sp_sr_sw.v create mode 100644 tests/asicworld/code_hdl_models_rom_using_case.v create mode 100644 tests/asicworld/code_hdl_models_serial_crc.v create mode 100644 tests/asicworld/code_hdl_models_t_gate_switch.v create mode 100644 tests/asicworld/code_hdl_models_tff_async_reset.v create mode 100644 tests/asicworld/code_hdl_models_tff_sync_reset.v create mode 100644 tests/asicworld/code_hdl_models_uart.v create mode 100644 tests/asicworld/code_hdl_models_up_counter.v create mode 100644 tests/asicworld/code_hdl_models_up_counter_load.v create mode 100644 tests/asicworld/code_hdl_models_up_down_counter.v create mode 100644 tests/asicworld/code_specman_switch_fabric.v create mode 100644 tests/asicworld/code_tidbits_asyn_reset.v create mode 100644 tests/asicworld/code_tidbits_blocking.v create mode 100644 tests/asicworld/code_tidbits_fsm_using_always.v create mode 100644 tests/asicworld/code_tidbits_fsm_using_function.v create mode 100644 tests/asicworld/code_tidbits_fsm_using_single_always.v create mode 100644 tests/asicworld/code_tidbits_nonblocking.v create mode 100644 tests/asicworld/code_tidbits_reg_combo_example.v create mode 100644 tests/asicworld/code_tidbits_reg_seq_example.v create mode 100644 tests/asicworld/code_tidbits_syn_reset.v create mode 100644 tests/asicworld/code_tidbits_wire_example.v create mode 100644 tests/asicworld/code_verilog_tutorial_addbit.v create mode 100644 tests/asicworld/code_verilog_tutorial_always_example.v create mode 100644 tests/asicworld/code_verilog_tutorial_bus_con.v create mode 100644 tests/asicworld/code_verilog_tutorial_comment.v create mode 100644 tests/asicworld/code_verilog_tutorial_counter.v create mode 100644 tests/asicworld/code_verilog_tutorial_counter_tb.v create mode 100644 tests/asicworld/code_verilog_tutorial_d_ff.v create mode 100644 tests/asicworld/code_verilog_tutorial_decoder.v create mode 100644 tests/asicworld/code_verilog_tutorial_decoder_always.v create mode 100644 tests/asicworld/code_verilog_tutorial_escape_id.v create mode 100644 tests/asicworld/code_verilog_tutorial_explicit.v create mode 100644 tests/asicworld/code_verilog_tutorial_first_counter.v create mode 100644 tests/asicworld/code_verilog_tutorial_first_counter_tb.v create mode 100644 tests/asicworld/code_verilog_tutorial_flip_flop.v create mode 100644 tests/asicworld/code_verilog_tutorial_fsm_full.v create mode 100644 tests/asicworld/code_verilog_tutorial_fsm_full_tb.v create mode 100644 tests/asicworld/code_verilog_tutorial_good_code.v create mode 100644 tests/asicworld/code_verilog_tutorial_if_else.v create mode 100644 tests/asicworld/code_verilog_tutorial_multiply.v create mode 100644 tests/asicworld/code_verilog_tutorial_mux_21.v create mode 100644 tests/asicworld/code_verilog_tutorial_n_out_primitive.v create mode 100644 tests/asicworld/code_verilog_tutorial_parallel_if.v create mode 100644 tests/asicworld/code_verilog_tutorial_parity.v create mode 100644 tests/asicworld/code_verilog_tutorial_simple_function.v create mode 100644 tests/asicworld/code_verilog_tutorial_simple_if.v create mode 100644 tests/asicworld/code_verilog_tutorial_task_global.v create mode 100644 tests/asicworld/code_verilog_tutorial_tri_buf.v create mode 100644 tests/asicworld/code_verilog_tutorial_v2k_reg.v create mode 100644 tests/asicworld/code_verilog_tutorial_which_clock.v create mode 100755 tests/asicworld/run-test.sh create mode 100644 tests/hana/README create mode 100644 tests/hana/hana_vlib.v create mode 100755 tests/hana/run-test.sh create mode 100644 tests/hana/test_intermout_always_comb_1_test.v create mode 100644 tests/hana/test_intermout_always_comb_3_test.v create mode 100644 tests/hana/test_intermout_always_comb_4_test.v create mode 100644 tests/hana/test_intermout_always_comb_5_test.v create mode 100644 tests/hana/test_intermout_always_ff_3_test.v create mode 100644 tests/hana/test_intermout_always_ff_4_test.v create mode 100644 tests/hana/test_intermout_always_ff_5_test.v create mode 100644 tests/hana/test_intermout_always_ff_6_test.v create mode 100644 tests/hana/test_intermout_always_ff_8_test.v create mode 100644 tests/hana/test_intermout_always_ff_9_test.v create mode 100644 tests/hana/test_intermout_always_latch_1_test.v create mode 100644 tests/hana/test_intermout_bufrm_1_test.v create mode 100644 tests/hana/test_intermout_bufrm_2_test.v create mode 100644 tests/hana/test_intermout_bufrm_6_test.v create mode 100644 tests/hana/test_intermout_bufrm_7_test.v create mode 100644 tests/hana/test_intermout_exprs_add_test.v create mode 100644 tests/hana/test_intermout_exprs_binlogic_test.v create mode 100644 tests/hana/test_intermout_exprs_bitwiseneg_test.v create mode 100644 tests/hana/test_intermout_exprs_buffer_test.v create mode 100644 tests/hana/test_intermout_exprs_condexpr_mux_test.v create mode 100644 tests/hana/test_intermout_exprs_condexpr_tribuf_test.v create mode 100644 tests/hana/test_intermout_exprs_const_test.v create mode 100644 tests/hana/test_intermout_exprs_constshift_test.v create mode 100644 tests/hana/test_intermout_exprs_div_test.v create mode 100644 tests/hana/test_intermout_exprs_logicneg_test.v create mode 100644 tests/hana/test_intermout_exprs_mod_test.v create mode 100644 tests/hana/test_intermout_exprs_mul_test.v create mode 100644 tests/hana/test_intermout_exprs_redand_test.v create mode 100644 tests/hana/test_intermout_exprs_redop_test.v create mode 100644 tests/hana/test_intermout_exprs_sub_test.v create mode 100644 tests/hana/test_intermout_exprs_unaryminus_test.v create mode 100644 tests/hana/test_intermout_exprs_unaryplus_test.v create mode 100644 tests/hana/test_intermout_exprs_varshift_test.v create mode 100644 tests/hana/test_parse2synthtrans_behavopt_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_case_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_contassign_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_module_basic0_test.v create mode 100644 tests/hana/test_parse2synthtrans_operators_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_param_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_port_scalar_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_port_vector_1_test.v create mode 100644 tests/hana/test_parse2synthtrans_v2k_comb_logic_sens_list_test.v create mode 100644 tests/hana/test_parser_constructs_module_basic1_test.v create mode 100644 tests/hana/test_parser_constructs_param_basic0_test.v create mode 100644 tests/hana/test_parser_constructs_port_basic0_test.v create mode 100644 tests/hana/test_parser_directives_define_simpledef_test.v create mode 100644 tests/hana/test_parser_misc_operators_test.v create mode 100644 tests/hana/test_parser_v2k_comb_port_data_type_test.v create mode 100644 tests/hana/test_parser_v2k_comma_sep_sens_list_test.v create mode 100644 tests/hana/test_simulation_always_15_test.v create mode 100644 tests/hana/test_simulation_always_17_test.v create mode 100644 tests/hana/test_simulation_always_18_test.v create mode 100644 tests/hana/test_simulation_always_19_test.v create mode 100644 tests/hana/test_simulation_always_1_test.v create mode 100644 tests/hana/test_simulation_always_20_test.v create mode 100644 tests/hana/test_simulation_always_21_test.v create mode 100644 tests/hana/test_simulation_always_22_test.v create mode 100644 tests/hana/test_simulation_always_23_test.v create mode 100644 tests/hana/test_simulation_always_27_test.v create mode 100644 tests/hana/test_simulation_always_29_test.v create mode 100644 tests/hana/test_simulation_always_31_tt.v create mode 100644 tests/hana/test_simulation_and_1_test.v create mode 100644 tests/hana/test_simulation_and_2_test.v create mode 100644 tests/hana/test_simulation_and_3_test.v create mode 100644 tests/hana/test_simulation_and_4_test.v create mode 100644 tests/hana/test_simulation_and_5_test.v create mode 100644 tests/hana/test_simulation_and_6_test.v create mode 100644 tests/hana/test_simulation_and_7_test.v create mode 100644 tests/hana/test_simulation_buffer_1_test.v create mode 100644 tests/hana/test_simulation_buffer_2_test.v create mode 100644 tests/hana/test_simulation_buffer_3_test.v create mode 100644 tests/hana/test_simulation_decoder_2_test.v create mode 100644 tests/hana/test_simulation_decoder_3_test.v create mode 100644 tests/hana/test_simulation_decoder_4_test.v create mode 100644 tests/hana/test_simulation_decoder_5_test.v create mode 100644 tests/hana/test_simulation_decoder_6_test.v create mode 100644 tests/hana/test_simulation_decoder_7_test.v create mode 100644 tests/hana/test_simulation_decoder_8_test.v create mode 100644 tests/hana/test_simulation_inc_16_test.v create mode 100644 tests/hana/test_simulation_inc_1_test.v create mode 100644 tests/hana/test_simulation_inc_2_test.v create mode 100644 tests/hana/test_simulation_inc_32_test.v create mode 100644 tests/hana/test_simulation_inc_4_test.v create mode 100644 tests/hana/test_simulation_inc_8_test.v create mode 100644 tests/hana/test_simulation_mod_1_xx.v create mode 100644 tests/hana/test_simulation_mux_16_test.v create mode 100644 tests/hana/test_simulation_mux_2_test.v create mode 100644 tests/hana/test_simulation_mux_32_test.v create mode 100644 tests/hana/test_simulation_mux_4_test.v create mode 100644 tests/hana/test_simulation_mux_64_test.v create mode 100644 tests/hana/test_simulation_mux_8_test.v create mode 100644 tests/hana/test_simulation_nand_1_test.v create mode 100644 tests/hana/test_simulation_nand_3_test.v create mode 100644 tests/hana/test_simulation_nand_4_test.v create mode 100644 tests/hana/test_simulation_nand_5_test.v create mode 100644 tests/hana/test_simulation_nand_6_test.v create mode 100644 tests/hana/test_simulation_nor_1_test.v create mode 100644 tests/hana/test_simulation_nor_2_test.v create mode 100644 tests/hana/test_simulation_nor_3_test.v create mode 100644 tests/hana/test_simulation_nor_4_test.v create mode 100644 tests/hana/test_simulation_opt_constprop_contassign_1_test.v create mode 100644 tests/hana/test_simulation_or_1_test.v create mode 100644 tests/hana/test_simulation_or_2_test.v create mode 100644 tests/hana/test_simulation_or_3_test.v create mode 100644 tests/hana/test_simulation_or_4_test.v create mode 100644 tests/hana/test_simulation_or_5_test.v create mode 100644 tests/hana/test_simulation_or_6_test.v create mode 100644 tests/hana/test_simulation_seq_ff_1_test.v create mode 100644 tests/hana/test_simulation_seq_ff_2_test.v create mode 100644 tests/hana/test_simulation_shifter_left_16_test.v create mode 100644 tests/hana/test_simulation_shifter_left_32_test.v create mode 100644 tests/hana/test_simulation_shifter_left_4_test.v create mode 100644 tests/hana/test_simulation_shifter_left_64_test.v create mode 100644 tests/hana/test_simulation_shifter_left_8_test.v create mode 100644 tests/hana/test_simulation_shifter_right_16_test.v create mode 100644 tests/hana/test_simulation_shifter_right_32_test.v create mode 100644 tests/hana/test_simulation_shifter_right_4_test.v create mode 100644 tests/hana/test_simulation_shifter_right_64_test.v create mode 100644 tests/hana/test_simulation_shifter_right_8_test.v create mode 100644 tests/hana/test_simulation_sop_basic_10_test.v create mode 100644 tests/hana/test_simulation_sop_basic_11_test.v create mode 100644 tests/hana/test_simulation_sop_basic_12_test.v create mode 100644 tests/hana/test_simulation_sop_basic_18_test.v create mode 100644 tests/hana/test_simulation_sop_basic_3_test.v create mode 100644 tests/hana/test_simulation_sop_basic_7_test.v create mode 100644 tests/hana/test_simulation_sop_basic_8_test.v create mode 100644 tests/hana/test_simulation_sop_basic_9_test.v create mode 100644 tests/hana/test_simulation_techmap_and_19_tech.v create mode 100644 tests/hana/test_simulation_techmap_and_5_tech.v create mode 100644 tests/hana/test_simulation_techmap_buf_test.v create mode 100644 tests/hana/test_simulation_techmap_inv_test.v create mode 100644 tests/hana/test_simulation_techmap_mux_0_test.v create mode 100644 tests/hana/test_simulation_techmap_mux_128_test.v create mode 100644 tests/hana/test_simulation_techmap_mux_8_test.v create mode 100644 tests/hana/test_simulation_techmap_nand_19_tech.v create mode 100644 tests/hana/test_simulation_techmap_nand_2_tech.v create mode 100644 tests/hana/test_simulation_techmap_nand_5_tech.v create mode 100644 tests/hana/test_simulation_techmap_nor_19_tech.v create mode 100644 tests/hana/test_simulation_techmap_nor_2_tech.v create mode 100644 tests/hana/test_simulation_techmap_nor_5_tech.v create mode 100644 tests/hana/test_simulation_techmap_or_19_tech.v create mode 100644 tests/hana/test_simulation_techmap_or_5_tech.v create mode 100644 tests/hana/test_simulation_techmap_xnor_2_tech.v create mode 100644 tests/hana/test_simulation_techmap_xnor_5_tech.v create mode 100644 tests/hana/test_simulation_techmap_xor_19_tech.v create mode 100644 tests/hana/test_simulation_techmap_xor_2_tech.v create mode 100644 tests/hana/test_simulation_techmap_xor_5_tech.v create mode 100644 tests/hana/test_simulation_tribuf_2_test.v create mode 100644 tests/hana/test_simulation_xnor_1_test.v create mode 100644 tests/hana/test_simulation_xnor_2_test.v create mode 100644 tests/hana/test_simulation_xnor_3_test.v create mode 100644 tests/hana/test_simulation_xnor_4_test.v create mode 100644 tests/hana/test_simulation_xor_1_test.v create mode 100644 tests/hana/test_simulation_xor_2_test.v create mode 100644 tests/hana/test_simulation_xor_3_test.v create mode 100644 tests/hana/test_simulation_xor_4_test.v create mode 100644 tests/i2c_bench/i2c_master_bit_ctrl.v create mode 100644 tests/i2c_bench/i2c_master_byte_ctrl.v create mode 100644 tests/i2c_bench/i2c_master_defines.v create mode 100644 tests/i2c_bench/i2c_master_top.v create mode 100644 tests/i2c_bench/i2c_slave_model.v create mode 100755 tests/i2c_bench/run-test.sh create mode 100644 tests/i2c_bench/spi_slave_model.v create mode 100644 tests/i2c_bench/timescale.v create mode 100644 tests/i2c_bench/tst_bench_top.v create mode 100644 tests/i2c_bench/wb_master_model.v create mode 100644 tests/iwls2005/README create mode 100644 tests/iwls2005/aes_core/aes_cipher_top.v create mode 100644 tests/iwls2005/aes_core/aes_inv_cipher_top.v create mode 100644 tests/iwls2005/aes_core/aes_inv_sbox.v create mode 100644 tests/iwls2005/aes_core/aes_key_expand_128.v create mode 100644 tests/iwls2005/aes_core/aes_rcon.v create mode 100644 tests/iwls2005/aes_core/aes_sbox.v create mode 100644 tests/iwls2005/aes_core/timescale.v create mode 100644 tests/iwls2005/fpu/except.v create mode 100644 tests/iwls2005/fpu/fpu.v create mode 100644 tests/iwls2005/fpu/post_norm.v create mode 100644 tests/iwls2005/fpu/pre_norm.v create mode 100644 tests/iwls2005/fpu/pre_norm_fmul.v create mode 100644 tests/iwls2005/fpu/primitives.v create mode 100644 tests/iwls2005/i2c/i2c_master_bit_ctrl.v create mode 100644 tests/iwls2005/i2c/i2c_master_byte_ctrl.v create mode 100644 tests/iwls2005/i2c/i2c_master_defines.v create mode 100644 tests/iwls2005/i2c/i2c_master_top.v create mode 100644 tests/iwls2005/i2c/timescale.v create mode 100755 tests/iwls2005/run-fm.sh create mode 100755 tests/iwls2005/run-synth.sh create mode 100644 tests/iwls2005/run-synth.ys create mode 100644 tests/iwls2005/sasc/sasc_brg.v create mode 100644 tests/iwls2005/sasc/sasc_fifo4.v create mode 100644 tests/iwls2005/sasc/sasc_top.v create mode 100644 tests/iwls2005/sasc/timescale.v create mode 100644 tests/iwls2005/simple_spi/fifo4.v create mode 100644 tests/iwls2005/simple_spi/simple_spi_top.v create mode 100644 tests/iwls2005/spi/spi_clgen.v create mode 100644 tests/iwls2005/spi/spi_defines.v create mode 100644 tests/iwls2005/spi/spi_shift.v create mode 100644 tests/iwls2005/spi/spi_top.v create mode 100644 tests/iwls2005/spi/timescale.v create mode 100644 tests/iwls2005/ss_pcm/pcm_slv_top.v create mode 100644 tests/iwls2005/ss_pcm/timescale.v create mode 100644 tests/iwls2005/systemcaes/aes.v create mode 100644 tests/iwls2005/systemcaes/byte_mixcolum.v create mode 100644 tests/iwls2005/systemcaes/keysched.v create mode 100644 tests/iwls2005/systemcaes/mixcolum.v create mode 100644 tests/iwls2005/systemcaes/sbox.v create mode 100644 tests/iwls2005/systemcaes/subbytes.v create mode 100644 tests/iwls2005/systemcaes/timescale.v create mode 100644 tests/iwls2005/systemcaes/word_mixcolum.v create mode 100644 tests/iwls2005/usb_phy/timescale.v create mode 100644 tests/iwls2005/usb_phy/usb_phy.v create mode 100644 tests/iwls2005/usb_phy/usb_rx_phy.v create mode 100644 tests/iwls2005/usb_phy/usb_tx_phy.v create mode 100644 tests/no-icarus/README create mode 100644 tests/no-icarus/autowire.v create mode 100644 tests/no-icarus/var_range.v create mode 100644 tests/openmsp430/rtl/omsp_alu.v create mode 100644 tests/openmsp430/rtl/omsp_and_gate.v create mode 100644 tests/openmsp430/rtl/omsp_clock_gate.v create mode 100644 tests/openmsp430/rtl/omsp_clock_module.v create mode 100644 tests/openmsp430/rtl/omsp_clock_mux.v create mode 100644 tests/openmsp430/rtl/omsp_dbg.v create mode 100644 tests/openmsp430/rtl/omsp_dbg_hwbrk.v create mode 100644 tests/openmsp430/rtl/omsp_dbg_uart.v create mode 100644 tests/openmsp430/rtl/omsp_execution_unit.v create mode 100644 tests/openmsp430/rtl/omsp_frontend.v create mode 100644 tests/openmsp430/rtl/omsp_mem_backbone.v create mode 100644 tests/openmsp430/rtl/omsp_multiplier.v create mode 100644 tests/openmsp430/rtl/omsp_register_file.v create mode 100644 tests/openmsp430/rtl/omsp_scan_mux.v create mode 100644 tests/openmsp430/rtl/omsp_sfr.v create mode 100644 tests/openmsp430/rtl/omsp_sync_cell.v create mode 100644 tests/openmsp430/rtl/omsp_sync_reset.v create mode 100644 tests/openmsp430/rtl/omsp_wakeup_cell.v create mode 100644 tests/openmsp430/rtl/omsp_watchdog.v create mode 100644 tests/openmsp430/rtl/openMSP430.v create mode 100644 tests/openmsp430/rtl/openMSP430_defines.v create mode 100644 tests/openmsp430/rtl/openMSP430_undefines.v create mode 100644 tests/openmsp430/run-fm.do create mode 100644 tests/openmsp430/run-fm.sh create mode 100644 tests/openmsp430/run-synth.sh create mode 100644 tests/openmsp430/run-synth.ys create mode 100644 tests/openmsp430/sim_mul.v create mode 100644 tests/or1200/config.patch create mode 100644 tests/or1200/run-checkout.sh create mode 100644 tests/or1200/run-fm-mods.sh create mode 100644 tests/or1200/run-fm.do create mode 100644 tests/or1200/run-fm.sh create mode 100644 tests/or1200/run-synth.sh create mode 100644 tests/or1200/run-synth.ys create mode 100644 tests/or1200/run-vg.sh create mode 100644 tests/simple/aes_kexp128.v create mode 100644 tests/simple/dff_different_styles.v create mode 100644 tests/simple/fiedler-cooley.v create mode 100644 tests/simple/fsm.v create mode 100644 tests/simple/generate.v create mode 100644 tests/simple/i2c_master_tests.v create mode 100644 tests/simple/loops.v create mode 100644 tests/simple/mem2reg.v create mode 100644 tests/simple/memory.v create mode 100644 tests/simple/muxtree.v create mode 100644 tests/simple/omsp_dbg_uart.v create mode 100644 tests/simple/operators.v create mode 100644 tests/simple/paramods.v create mode 100644 tests/simple/process.v create mode 100755 tests/simple/run-test.sh create mode 100644 tests/simple/subbytes.v create mode 100644 tests/simple/task_func.v create mode 100644 tests/simple/usb_phy_tetsts.v create mode 100644 tests/simple/values.v create mode 100755 tests/tools/autotest.sh create mode 100644 tests/tools/cmp_tbdata.c create mode 100755 tests/tools/profiler.pl create mode 100755 tests/tools/rtlview.sh create mode 100755 tests/tools/vcdcd.pl diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..49533d068 --- /dev/null +++ b/Makefile @@ -0,0 +1,76 @@ + +CONFIG := clang-debug +# CONFIG := gcc-debug +# CONFIG := release + +OBJS = kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/sha1.o kernel/calc.o kernel/select.o kernel/show.o +OBJS += bigint/BigIntegerAlgorithms.o bigint/BigInteger.o bigint/BigIntegerUtils.o bigint/BigUnsigned.o bigint/BigUnsignedInABase.o + +GENFILES = +TARGETS = yosys + +all: top-all + +CXXFLAGS = -Wall -Wextra -ggdb -I$(shell pwd) -MD +LDFLAGS = +LDLIBS = -lstdc++ -lreadline -lm + +-include Makefile.conf + +ifeq ($(CONFIG),clang-debug) +CXX = clang +CXXFLAGS += -std=c++11 -O0 +endif + +ifeq ($(CONFIG),gcc-debug) +CXX = gcc +CXXFLAGS += -std=gnu++0x -O0 +endif + +ifeq ($(CONFIG),release) +CXX = gcc +CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG +endif + +include frontends/*/Makefile.inc +include passes/*/Makefile.inc +include backends/*/Makefile.inc +include techlibs/Makefile.inc + +top-all: $(TARGETS) + +yosys: $(OBJS) + $(CXX) -o yosys $(LDFLAGS) $(OBJS) $(LDLIBS) + +test: yosys + cd tests/simple && bash run-test.sh + cd tests/hana && bash run-test.sh + cd tests/asicworld && bash run-test.sh + +help: + @find -name '*.cc' | xargs egrep -h '(Pass|Frontend|Backend)\(".*"\)' | \ + sed 's,.*: ,,; s, .*,,;' | sort | tr '\n' '\t' | expand -t25 | fmt + +install: yosys + install yosys /usr/local/bin/yosys + +clean: + rm -f $(OBJS) $(GENFILES) $(TARGETS) + rm -f bigint/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d + +mrproper: clean + svn st --no-ignore | grep '^[?I]' | cut -c8- | sed 's,^ *,,; /^Makefile.conf$$/ d;' | xargs -r -d '\n' rm -vrf + +qtcreator: + { for file in $(basename $(OBJS)); do \ + for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \ + done; find backends bigint frontends kernel passes -type f \( -name '*.h' -o -name '*.hh' \); } > qtcreator.files + { echo .; find backends bigint frontends kernel passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes + touch qtcreator.config qtcreator.creator + +-include bigint/*.d +-include frontends/*/*.d +-include passes/*/*.d +-include backends/*/*.d +-include kernel/*.d + diff --git a/README b/README new file mode 100644 index 000000000..0a519262c --- /dev/null +++ b/README @@ -0,0 +1,97 @@ + +yosys -- Yosys Open SYnthesis Suite +=================================== + +This is a framework for RTL synthesis tools. It is highly +experimental and under construction. The goal for now is +to implement an extensible Verilog-2005 synthesis tool. + +The aim of this tool is to generate valid logic netlists +from HDL designs in a manner that allows for easy addition +of extra synthesis passes. This tool does not aim at generating +efficient logic netlists. This can be done by passing the +output of Yosys to a low-level synthesis tool such as ABC. + +Yosys is free software licensed under the ISC license (a GPL +compatible licence that is similar in terms to the MIT license +or the 2-clause BSD license). + + +Unsupported Verilog-2005 Features +================================= + +The following Verilog-2005 features are not supported by +yosys and there are currently no plans to add support +for them: + +- Non-sythesizable language features as defined in + IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 + +- The "tri", "triand", "trior", "wand" and "wor" net types + +- The "library" and "configuration" source file formats + +- The "disable" and "primitive" statements + +- Latched logic (is synthesized as logic with feedback loops) + + +Verilog Attributes and non-standard features +============================================ + +- The 'full_case' attribute on case statements is supported + (also the non-standard "// synopsys full_case" directive) + +- The "// synopsys translate_off" and "// synopsys translate_on" + directives are also supported (but the use of `ifdef .. `endif + is strongly recommended instead). + +- The "nomem2reg" attribute on modules or arrays prohibits the + automatic early conversion of arrays to seperate registers. + +- The "nolatches" attribute on modules or always-blocks + prohibits the generation of logic-loops for latches. Instead + all not explicitly assigned values default to x-bits. + +- In addition to the (* ... *) attribute syntax, yosys supports + the non-standard {* ... *} attribute syntax to set default attributes + for everything that comes after the {* ... *} statement. (Reset + by adding an empty {* *} statement.) The preprocessor define + __YOSYS_ENABLE_DEFATTR__ must be set in order for this featre to be active. + + +TODOs / Open Bugs +================= + +- Write "design and implementation of.." document + +- Add brief sourcecode documentation to: + + - Most passes and kernel functionalities + +- Implement missing Verilog 2005 features: + + - Signed constants + - ROM modelling using "initial" blocks + - Builtin primitive gates (and, nand, cmos, nmos, pmos, etc..) + - Ignore what needs to be ignored (e.g. drive and charge strenghts) + - Check standard vs. implementation to identify missing features + +- Actually use range information on parameters + +- Implement mux-to-tribuf pass and rebalance mixed mux/tribuf trees + +- TCL and Python interfaces to frontends, passes, backends and RTLIL + +- Additional internal cell types: $bitcount, $pla, $lut and $pmux + +- Subsystem for selecting stuff (and limiting scope of passes) + +- Support for registering designs (as collection of modules) to CellTypes + +- Kernel support for collections of cells (from input/output cones, etc) + +- Smarter resource sharing pass (add MUXes and get rid of duplicated cells) + +- FSM state encoding and technology mapping + diff --git a/backends/autotest/Makefile.inc b/backends/autotest/Makefile.inc new file mode 100644 index 000000000..9308dcd47 --- /dev/null +++ b/backends/autotest/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/autotest/autotest.o + diff --git a/backends/autotest/autotest.cc b/backends/autotest/autotest.cc new file mode 100644 index 000000000..36d5650fc --- /dev/null +++ b/backends/autotest/autotest.cc @@ -0,0 +1,309 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +#define NUM_ITER 1000 + +static std::string id(std::string internal_id) +{ + const char *str = internal_id.c_str(); + bool do_escape = false; + + if (*str == '\\') + str++; + + if ('0' <= *str && *str <= '9') + do_escape = true; + + for (int i = 0; str[i]; i++) { + if ('0' <= str[i] && str[i] <= '9') + continue; + if ('a' <= str[i] && str[i] <= 'z') + continue; + if ('A' <= str[i] && str[i] <= 'Z') + continue; + if (str[i] == '_') + continue; + do_escape = true; + break; + } + + if (do_escape) + return "\\" + std::string(str) + " "; + return std::string(str); +} + +static std::string idx(std::string str) +{ + if (str[0] == '\\') + return str.substr(1); + return str; +} + +static std::string idy(std::string str1, std::string str2 = std::string(), std::string str3 = std::string()) +{ + str1 = idx(str1); + if (!str2.empty()) + str1 += "_" + idx(str2); + if (!str3.empty()) + str1 += "_" + idx(str3); + return id(str1); +} + +static void autotest(FILE *f, RTLIL::Design *design) +{ + fprintf(f, "module testbench;\n\n"); + + fprintf(f, "integer i;\n\n"); + + fprintf(f, "reg [31:0] xorshift128_x = 123456789;\n"); + fprintf(f, "reg [31:0] xorshift128_y = 362436069;\n"); + fprintf(f, "reg [31:0] xorshift128_z = 521288629;\n"); + fprintf(f, "reg [31:0] xorshift128_w = 88675123;\n"); + fprintf(f, "reg [31:0] xorshift128_t;\n\n"); + fprintf(f, "task xorshift128;\n"); + fprintf(f, "begin\n"); + fprintf(f, "\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n"); + fprintf(f, "\txorshift128_x = xorshift128_y;\n"); + fprintf(f, "\txorshift128_y = xorshift128_z;\n"); + fprintf(f, "\txorshift128_z = xorshift128_w;\n"); + fprintf(f, "\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n"); + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + + for (auto it = design->modules.begin(); it != design->modules.end(); it++) + { + std::map signal_in; + std::map signal_const; + std::map signal_clk; + std::map signal_out; + + RTLIL::Module *mod = it->second; + int count_ports = 0; + log("Generating test bench for module `%s'.\n", it->first.c_str()); + for (auto it2 = mod->wires.begin(); it2 != mod->wires.end(); it2++) { + RTLIL::Wire *wire = it2->second; + if (wire->port_output) { + count_ports++; + signal_out[idy("sig", mod->name, wire->name)] = wire->width; + fprintf(f, "wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name, wire->name).c_str()); + } else if (wire->port_input) { + count_ports++; + bool is_clksignal = wire->attributes.count("\\gentb_clock") > 0; + for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); it3++) + for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); it4++) { + if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1) + continue; + RTLIL::SigSpec &signal = (*it4)->signal; + for (size_t i = 0; i < signal.chunks.size(); i++) { + if (signal.chunks[i].wire == wire) + is_clksignal = true; + } + } + if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) { + signal_clk[idy("sig", mod->name, wire->name)] = wire->width; + } else { + signal_in[idy("sig", mod->name, wire->name)] = wire->width; + if (wire->attributes.count("\\gentb_constant") > 0) + signal_const[idy("sig", mod->name, wire->name)] = wire->attributes["\\gentb_constant"].as_string(); + } + fprintf(f, "reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name, wire->name).c_str()); + } + } + fprintf(f, "%s %s(\n", id(mod->name).c_str(), idy("uut", mod->name).c_str()); + for (auto it2 = mod->wires.begin(); it2 != mod->wires.end(); it2++) { + RTLIL::Wire *wire = it2->second; + if (wire->port_output || wire->port_input) + fprintf(f, "\t.%s(%s)%s\n", id(wire->name).c_str(), + idy("sig", mod->name, wire->name).c_str(), --count_ports ? "," : ""); + } + fprintf(f, ");\n\n"); + + fprintf(f, "task %s;\n", idy(mod->name, "reset").c_str()); + fprintf(f, "begin\n"); + int delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) + fprintf(f, "\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) + fprintf(f, "\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + fprintf(f, "\t#100; %s <= 1;\n", it->first.c_str()); + fprintf(f, "\t#100; %s <= 0;\n", it->first.c_str()); + } + delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) + fprintf(f, "\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + fprintf(f, "\t#100; %s <= 1;\n", it->first.c_str()); + fprintf(f, "\t#100; %s <= 0;\n", it->first.c_str()); + } + delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) { + if (signal_const.count(it->first) == 0) + continue; + fprintf(f, "\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str()); + } + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + + fprintf(f, "task %s;\n", idy(mod->name, "update_data").c_str()); + fprintf(f, "begin\n"); + delay_counter = 0; + for (auto it = signal_in.begin(); it != signal_in.end(); it++) { + if (signal_const.count(it->first) > 0) + continue; + fprintf(f, "\txorshift128;\n"); + fprintf(f, "\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2); + } + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + + fprintf(f, "task %s;\n", idy(mod->name, "update_clock").c_str()); + fprintf(f, "begin\n"); + if (signal_clk.size()) { + fprintf(f, "\txorshift128;\n"); + fprintf(f, "\t{"); + int total_clock_bits = 0; + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); + total_clock_bits += it->second; + } + fprintf(f, " } = {"); + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) + fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); + fprintf(f, " } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits); + } + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + + char shorthand = 'A'; + std::vector header1; + std::string header2 = ""; + + fprintf(f, "task %s;\n", idy(mod->name, "print_status").c_str()); + fprintf(f, "begin\n"); + fprintf(f, "\t$display(\"%%b %%b %%b %%t %%d\", {"); + if (signal_in.size()) + for (auto it = signal_in.begin(); it != signal_in.end(); it++) { + fprintf(f, "%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str()); + int len = it->second; + if (len > 1) + header2 += "/", len--; + while (len > 1) + header2 += "-", len--; + if (len > 0) + header2 += shorthand, len--; + header1.push_back(" " + it->first); + header1.back()[0] = shorthand++; + } + else { + fprintf(f, " 1'bx"); + header2 += "#"; + } + fprintf(f, " }, {"); + header2 += " "; + if (signal_clk.size()) { + for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { + fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); + int len = it->second; + if (len > 1) + header2 += "/", len--; + while (len > 1) + header2 += "-", len--; + if (len > 0) + header2 += shorthand, len--; + header1.push_back(" " + it->first); + header1.back()[0] = shorthand++; + } + } else { + fprintf(f, " 1'bx"); + header2 += "#"; + } + fprintf(f, " }, {"); + header2 += " "; + if (signal_out.size()) { + for (auto it = signal_out.begin(); it != signal_out.end(); it++) { + fprintf(f, "%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str()); + int len = it->second; + if (len > 1) + header2 += "/", len--; + while (len > 1) + header2 += "-", len--; + if (len > 0) + header2 += shorthand, len--; + header1.push_back(" " + it->first); + header1.back()[0] = shorthand++; + } + } else { + fprintf(f, " 1'bx"); + header2 += "#"; + } + fprintf(f, " }, $time, i);\n"); + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + + fprintf(f, "task %s;\n", idy(mod->name, "print_header").c_str()); + fprintf(f, "begin\n"); + fprintf(f, "\t$display();\n"); + for (auto &hdr : header1) + fprintf(f, "\t$display(\" %s\");\n", hdr.c_str()); + fprintf(f, "\t$display();\n"); + fprintf(f, "\t$display(\"%s\");\n", header2.c_str()); + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + + fprintf(f, "task %s;\n", idy(mod->name, "test").c_str()); + fprintf(f, "begin\n"); + fprintf(f, "\t$display(\"\\n==== %s ====\");\n", idy(mod->name).c_str()); + fprintf(f, "\t%s;\n", idy(mod->name, "reset").c_str()); + fprintf(f, "\tfor (i=0; i<%d; i=i+1) begin\n", NUM_ITER); + fprintf(f, "\t\tif (i %% 20 == 0) %s;\n", idy(mod->name, "print_header").c_str()); + fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "update_data").c_str()); + fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "update_clock").c_str()); + fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "print_status").c_str()); + fprintf(f, "\tend\n"); + fprintf(f, "end\n"); + fprintf(f, "endtask\n\n"); + } + + fprintf(f, "initial begin\n"); + fprintf(f, "\t// $dumpfile(\"testbench.vcd\");\n"); + fprintf(f, "\t// $dumpvars(0, testbench);\n"); + for (auto it = design->modules.begin(); it != design->modules.end(); it++) + fprintf(f, "\t%s;\n", idy(it->first, "test").c_str()); + fprintf(f, "\t$finish;\n"); + fprintf(f, "end\n\n"); + + fprintf(f, "endmodule\n"); +} + +struct AutotestBackend : public Backend { + AutotestBackend() : Backend("autotest") { } + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + log_header("Executing AUTOTEST backend (auto-generate pseudo-random test benches).\n"); + extra_args(f, filename, args, 1); + autotest(f, design); + } +} AutotestBackend; + diff --git a/backends/ilang/Makefile.inc b/backends/ilang/Makefile.inc new file mode 100644 index 000000000..52fc2b891 --- /dev/null +++ b/backends/ilang/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/ilang/ilang_backend.o + diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc new file mode 100644 index 000000000..7e2837236 --- /dev/null +++ b/backends/ilang/ilang_backend.cc @@ -0,0 +1,306 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A very simple and straightforward backend for the RTLIL text + * representation (as understood by the 'ilang' frontend). + * + */ + +#include "ilang_backend.h" +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +using namespace ILANG_BACKEND; + +void ILANG_BACKEND::dump_const(FILE *f, const RTLIL::Const &data, int width, int offset, bool autoint) +{ + if (width < 0) + width = data.bits.size() - offset; + if (data.str.empty() || width != (int)data.bits.size()) { + if (width == 32 && autoint) { + int32_t val = 0; + for (int i = 0; i < width; i++) { + assert(offset+i < (int)data.bits.size()); + switch (data.bits[offset+i]) { + case RTLIL::S0: break; + case RTLIL::S1: val |= 1 << i; break; + default: val = -1; break; + } + } + if (val >= 0) { + fprintf(f, "%d", val); + return; + } + } + fprintf(f, "%d'", width); + for (int i = offset+width-1; i >= offset; i--) { + assert(i < (int)data.bits.size()); + switch (data.bits[i]) { + case RTLIL::S0: fprintf(f, "0"); break; + case RTLIL::S1: fprintf(f, "1"); break; + case RTLIL::Sx: fprintf(f, "x"); break; + case RTLIL::Sz: fprintf(f, "z"); break; + case RTLIL::Sa: fprintf(f, "-"); break; + case RTLIL::Sm: fprintf(f, "m"); break; + } + } + } else { + fprintf(f, "\""); + for (size_t i = 0; i < data.str.size(); i++) { + if (data.str[i] == '\n') + fprintf(f, "\\n"); + else if (data.str[i] == '\t') + fprintf(f, "\\t"); + else if (data.str[i] < 32) + fprintf(f, "\\%03o", data.str[i]); + else if (data.str[i] == '"') + fprintf(f, "\\\""); + else + fputc(data.str[i], f); + } + fprintf(f, "\""); + } +} + +void ILANG_BACKEND::dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint) +{ + if (chunk.wire == NULL) { + dump_const(f, chunk.data, chunk.width, chunk.offset, autoint); + } else { + if (chunk.width == chunk.wire->width && chunk.offset == 0) + fprintf(f, "%s", chunk.wire->name.c_str()); + else if (chunk.width == 1) + fprintf(f, "%s [%d]", chunk.wire->name.c_str(), chunk.offset); + else + fprintf(f, "%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset); + } +} + +void ILANG_BACKEND::dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint) +{ + if (sig.chunks.size() == 1) { + dump_sigchunk(f, sig.chunks[0], autoint); + } else { + fprintf(f, "{ "); + for (auto it = sig.chunks.rbegin(); it != sig.chunks.rend(); it++) { + dump_sigchunk(f, *it, false); + fprintf(f, " "); + } + fprintf(f, "}"); + } +} + +void ILANG_BACKEND::dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire) +{ + for (auto it = wire->attributes.begin(); it != wire->attributes.end(); it++) { + fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + fprintf(f, "%s" "wire ", indent.c_str()); + if (wire->auto_width) + fprintf(f, "auto "); + if (wire->width != 1) + fprintf(f, "width %d ", wire->width); + if (wire->start_offset != 0) + fprintf(f, "offset %d ", wire->start_offset); + if (wire->port_input && !wire->port_output) + fprintf(f, "input %d ", wire->port_id); + if (!wire->port_input && wire->port_output) + fprintf(f, "output %d ", wire->port_id); + if (wire->port_input && wire->port_output) + fprintf(f, "inout %d ", wire->port_id); + fprintf(f, "%s\n", wire->name.c_str()); +} + +void ILANG_BACKEND::dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory) +{ + for (auto it = memory->attributes.begin(); it != memory->attributes.end(); it++) { + fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + fprintf(f, "%s" "memory ", indent.c_str()); + if (memory->width != 1) + fprintf(f, "width %d ", memory->width); + if (memory->size != 0) + fprintf(f, "size %d ", memory->size); + fprintf(f, "%s\n", memory->name.c_str()); +} + +void ILANG_BACKEND::dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell) +{ + for (auto it = cell->attributes.begin(); it != cell->attributes.end(); it++) { + fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + fprintf(f, "%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str()); + for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) { + fprintf(f, "%s parameter %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) { + fprintf(f, "%s connect %s ", indent.c_str(), it->first.c_str()); + dump_sigspec(f, it->second); + fprintf(f, "\n"); + } + fprintf(f, "%s" "end\n", indent.c_str()); +} + +void ILANG_BACKEND::dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs) +{ + for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) + { + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, it->first); + fprintf(f, " "); + dump_sigspec(f, it->second); + fprintf(f, "\n"); + } + + for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + dump_proc_switch(f, indent, *it); +} + +void ILANG_BACKEND::dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw) +{ + for (auto it = sw->attributes.begin(); it != sw->attributes.end(); it++) { + fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + + fprintf(f, "%s" "switch ", indent.c_str()); + dump_sigspec(f, sw->signal); + fprintf(f, "\n"); + + for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) + { + fprintf(f, "%s case ", indent.c_str()); + for (size_t i = 0; i < (*it)->compare.size(); i++) { + if (i > 0) + fprintf(f, ", "); + dump_sigspec(f, (*it)->compare[i]); + } + fprintf(f, "\n"); + + dump_proc_case_body(f, indent + " ", *it); + } + + fprintf(f, "%s" "end\n", indent.c_str()); +} + +void ILANG_BACKEND::dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy) +{ + fprintf(f, "%s" "sync ", indent.c_str()); + switch (sy->type) { + if (0) case RTLIL::ST0: fprintf(f, "low "); + if (0) case RTLIL::ST1: fprintf(f, "high "); + if (0) case RTLIL::STp: fprintf(f, "posedge "); + if (0) case RTLIL::STn: fprintf(f, "negedge "); + if (0) case RTLIL::STe: fprintf(f, "edge "); + dump_sigspec(f, sy->signal); + fprintf(f, "\n"); + break; + case RTLIL::STa: fprintf(f, "always\n"); break; + } + + for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) { + fprintf(f, "%s update ", indent.c_str()); + dump_sigspec(f, it->first); + fprintf(f, " "); + dump_sigspec(f, it->second); + fprintf(f, "\n"); + } +} + +void ILANG_BACKEND::dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc) +{ + for (auto it = proc->attributes.begin(); it != proc->attributes.end(); it++) { + fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + fprintf(f, "%s" "process %s\n", indent.c_str(), proc->name.c_str()); + dump_proc_case_body(f, indent + " ", &proc->root_case); + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++) + dump_proc_sync(f, indent + " ", *it); + fprintf(f, "%s" "end\n", indent.c_str()); +} + +void ILANG_BACKEND::dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) +{ + fprintf(f, "%s" "connect ", indent.c_str()); + dump_sigspec(f, left); + fprintf(f, " "); + dump_sigspec(f, right); + fprintf(f, "\n"); +} + +void ILANG_BACKEND::dump_module(FILE *f, std::string indent, const RTLIL::Module *module) +{ + for (auto it = module->attributes.begin(); it != module->attributes.end(); it++) { + fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str()); + dump_const(f, it->second); + fprintf(f, "\n"); + } + + fprintf(f, "%s" "module %s\n", indent.c_str(), module->name.c_str()); + + for (auto it = module->wires.begin(); it != module->wires.end(); it++) + dump_wire(f, indent + " ", it->second); + + for (auto it = module->memories.begin(); it != module->memories.end(); it++) + dump_memory(f, indent + " ", it->second); + + for (auto it = module->cells.begin(); it != module->cells.end(); it++) + dump_cell(f, indent + " ", it->second); + + for (auto it = module->processes.begin(); it != module->processes.end(); it++) + dump_proc(f, indent + " ", it->second); + + for (auto it = module->connections.begin(); it != module->connections.end(); it++) + dump_conn(f, indent + " ", it->first, it->second); + + fprintf(f, "%s" "end\n", indent.c_str()); +} + +void ILANG_BACKEND::dump_design(FILE *f, const RTLIL::Design *design) +{ + for (auto it = design->modules.begin(); it != design->modules.end(); it++) { + if (it != design->modules.begin()) + fprintf(f, "\n"); + dump_module(f, "", it->second); + } +} + +struct IlangBackend : public Backend { + IlangBackend() : Backend("ilang") { } + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) { + log_header("Executing ILANG backend.\n"); + extra_args(f, filename, args, 1); + log("Output filename: %s\n", filename.c_str()); + ILANG_BACKEND::dump_design(f, design); + } +} IlangBackend; + diff --git a/backends/ilang/ilang_backend.h b/backends/ilang/ilang_backend.h new file mode 100644 index 000000000..e34c4e675 --- /dev/null +++ b/backends/ilang/ilang_backend.h @@ -0,0 +1,47 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A very simple and straightforward backend for the RTLIL text + * representation (as understood by the 'ilang' frontend). + * + */ + +#ifndef ILANG_BACKEND_H +#define ILANG_BACKEND_H + +#include "kernel/rtlil.h" +#include + +namespace ILANG_BACKEND { + void dump_const(FILE *f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true); + void dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint = true); + void dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint = true); + void dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire); + void dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory); + void dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell); + void dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs); + void dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw); + void dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy); + void dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc); + void dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right); + void dump_module(FILE *f, std::string indent, const RTLIL::Module *module); + void dump_design(FILE *f, const RTLIL::Design *design); +} + +#endif diff --git a/backends/verilog/Makefile.inc b/backends/verilog/Makefile.inc new file mode 100644 index 000000000..c2dffef7a --- /dev/null +++ b/backends/verilog/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/verilog/verilog_backend.o + diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc new file mode 100644 index 000000000..d79908006 --- /dev/null +++ b/backends/verilog/verilog_backend.cc @@ -0,0 +1,905 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A simple and straightforward verilog backend. + * + * Note that RTLIL processes can't always be mapped easily to a Verilog + * process. Therefore this frontend should only be used to export a + * Verilog netlist (i.e. after the "proc" pass has converted all processes + * to logic networks and registers). + * + */ + +#include "verilog_backend.h" +#include "kernel/register.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include +#include +#include +#include +#include + +namespace { + +bool norename, noattr, attr2comment, noexpr; +int auto_name_counter, auto_name_offset, auto_name_digits; +std::map auto_name_map; + +std::set reg_wires; + +CellTypes reg_ct; +RTLIL::Module *active_module; + +void reset_auto_counter_id(const std::string &id, bool may_rename) +{ + const char *str = id.c_str(); + + if (*str == '$' && may_rename && !norename) + auto_name_map[id] = auto_name_counter++; + + if (str[0] != '_' && str[1] != 0) + return; + for (int i = 0; str[i] != 0; i++) { + if (str[i] == '_') + continue; + if (str[i] < '0' || str[i] > '9') + return; + } + + int num = atoi(str+1); + if (num >= auto_name_offset) + auto_name_offset = num + 1; +} + +void reset_auto_counter(RTLIL::Module *module) +{ + auto_name_map.clear(); + auto_name_counter = 0; + auto_name_offset = 0; + + reset_auto_counter_id(module->name, false); + + for (auto it = module->wires.begin(); it != module->wires.end(); it++) + reset_auto_counter_id(it->second->name, true); + + for (auto it = module->cells.begin(); it != module->cells.end(); it++) { + reset_auto_counter_id(it->second->name, true); + reset_auto_counter_id(it->second->type, false); + } + + for (auto it = module->processes.begin(); it != module->processes.end(); it++) + reset_auto_counter_id(it->second->name, false); + + auto_name_digits = 1; + for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10) + auto_name_digits++; + + for (auto it = auto_name_map.begin(); it != auto_name_map.end(); it++) + log(" renaming `%s' to `_%0*d_'.\n", it->first.c_str(), auto_name_digits, auto_name_offset + it->second); +} + +std::string id(std::string internal_id, bool may_rename = true) +{ + const char *str = internal_id.c_str(); + bool do_escape = false; + + if (may_rename && auto_name_map.count(internal_id) != 0) { + char buffer[100]; + snprintf(buffer, 100, "_%0*d_", auto_name_digits, auto_name_offset + auto_name_map[internal_id]); + return std::string(buffer); + } + + if (*str == '\\') + str++; + + if ('0' <= *str && *str <= '9') + do_escape = true; + + for (int i = 0; str[i]; i++) + { + if ('0' <= str[i] && str[i] <= '9') + continue; + if ('a' <= str[i] && str[i] <= 'z') + continue; + if ('A' <= str[i] && str[i] <= 'Z') + continue; + if (str[i] == '_') + continue; + do_escape = true; + break; + } + + if (do_escape) + return "\\" + std::string(str) + " "; + return std::string(str); +} + +bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) +{ + sig.optimize(); + if (sig.chunks.size() != 1 || sig.chunks[0].wire == NULL) + return false; + if (reg_wires.count(sig.chunks[0].wire->name) == 0) + return false; + reg_name = id(sig.chunks[0].wire->name); + if (sig.width != sig.chunks[0].wire->width) + if (sig.width == 1) + reg_name += stringf("[%d]", sig.chunks[0].wire->start_offset + sig.chunks[0].offset); + else + reg_name += stringf("[%d]", sig.chunks[0].wire->start_offset + sig.chunks[0].offset + sig.chunks[0].width - 1, + sig.chunks[0].wire->start_offset + sig.chunks[0].offset); + return true; +} + +void dump_const(FILE *f, RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false) +{ + if (width < 0) + width = data.bits.size() - offset; + if (data.str.empty() || width != (int)data.bits.size()) { + if (width == 32 && !no_decimal) { + uint32_t val = 0; + for (int i = offset+width-1; i >= offset; i--) { + assert(i < (int)data.bits.size()); + if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1) + goto dump_bits; + if (data.bits[i] == RTLIL::S1) + val |= 1 << (i - offset); + } + fprintf(f, "%d", (int)val); + } else { + dump_bits: + fprintf(f, "%d'b", width); + for (int i = offset+width-1; i >= offset; i--) { + assert(i < (int)data.bits.size()); + switch (data.bits[i]) { + case RTLIL::S0: fprintf(f, "0"); break; + case RTLIL::S1: fprintf(f, "1"); break; + case RTLIL::Sx: fprintf(f, "x"); break; + case RTLIL::Sz: fprintf(f, "z"); break; + case RTLIL::Sa: fprintf(f, "z"); break; + case RTLIL::Sm: log_error("Found marker state in final netlist."); + } + } + } + } else { + fprintf(f, "\""); + for (size_t i = 0; i < data.str.size(); i++) { + if (data.str[i] == '\n') + fprintf(f, "\\n"); + else if (data.str[i] == '\t') + fprintf(f, "\\t"); + else if (data.str[i] < 32) + fprintf(f, "\\%03o", data.str[i]); + else if (data.str[i] == '"') + fprintf(f, "\\\""); + else + fputc(data.str[i], f); + } + fprintf(f, "\""); + } +} + +void dump_sigchunk(FILE *f, RTLIL::SigChunk &chunk, bool no_decimal = false) +{ + if (chunk.wire == NULL) { + dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); + } else { + if (chunk.width == chunk.wire->width && chunk.offset == 0) + fprintf(f, "%s", id(chunk.wire->name).c_str()); + else if (chunk.width == 1) + fprintf(f, "%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); + else + fprintf(f, "%s[%d:%d]", id(chunk.wire->name).c_str(), + chunk.offset + chunk.wire->start_offset + chunk.width - 1, + chunk.offset + chunk.wire->start_offset); + } +} + +void dump_sigspec(FILE *f, RTLIL::SigSpec &sig) +{ + if (sig.chunks.size() == 1) { + dump_sigchunk(f, sig.chunks[0]); + } else { + fprintf(f, "{ "); + for (auto it = sig.chunks.rbegin(); it != sig.chunks.rend(); it++) { + if (it != sig.chunks.rbegin()) + fprintf(f, ", "); + dump_sigchunk(f, *it, true); + } + fprintf(f, " }"); + } +} + +void dump_attributes(FILE *f, std::string indent, std::map &attributes, char term = '\n') +{ + if (noattr) + return; + for (auto it = attributes.begin(); it != attributes.end(); it++) { + fprintf(f, "%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); + if (it->second.bits.size() > 0) { + fprintf(f, " = "); + dump_const(f, it->second); + } + fprintf(f, " %s%c", attr2comment ? "*/" : "*)", term); + } +} + +void dump_wire(FILE *f, std::string indent, RTLIL::Wire *wire) +{ + dump_attributes(f, indent, wire->attributes); + if (wire->port_input && !wire->port_output) + fprintf(f, "%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + else if (!wire->port_input && wire->port_output) + fprintf(f, "%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + else if (wire->port_input && wire->port_output) + fprintf(f, "%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + else + fprintf(f, "%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire"); + if (wire->width != 1) + fprintf(f, "[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset); + fprintf(f, "%s;\n", id(wire->name).c_str()); +} + +void dump_memory(FILE *f, std::string indent, RTLIL::Memory *memory) +{ + dump_attributes(f, indent, memory->attributes); + fprintf(f, "%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1); +} + +void dump_cell_expr_port(FILE *f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) +{ + if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { + fprintf(f, "$signed("); + dump_sigspec(f, cell->connections["\\" + port]); + fprintf(f, ")"); + } else + dump_sigspec(f, cell->connections["\\" + port]); +} + +std::string cellname(RTLIL::Cell *cell) +{ + if (!norename && cell->name[0] == '$' && reg_ct.cell_known(cell->type) && cell->connections.count("\\Q") > 0) + { + RTLIL::SigSpec sig = cell->connections["\\Q"]; + if (sig.width != 1 || sig.is_fully_const()) + goto no_special_reg_name; + + sig.optimize(); + RTLIL::Wire *wire = sig.chunks[0].wire; + + if (wire->name[0] != '\\') + goto no_special_reg_name; + + std::string cell_name = wire->name; + + size_t pos = cell_name.find('['); + if (pos != std::string::npos) + cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos); + else + cell_name = cell_name + "_reg"; + + if (wire->width != 1) + cell_name += stringf("[%d]", wire->start_offset + sig.chunks[0].offset); + + if (active_module && active_module->count_id(cell_name) > 0) + goto no_special_reg_name; + + return id(cell_name); + } + else + { +no_special_reg_name: + return id(cell->name).c_str(); + } +} + +void dump_cell_expr_uniop(FILE *f, std::string indent, RTLIL::Cell *cell, std::string op) +{ + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Y"]); + fprintf(f, " = %s ", op.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", true); + fprintf(f, ";\n"); +} + +void dump_cell_expr_binop(FILE *f, std::string indent, RTLIL::Cell *cell, std::string op) +{ + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Y"]); + fprintf(f, " = "); + dump_cell_expr_port(f, cell, "A", true); + fprintf(f, " %s ", op.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", true); + fprintf(f, ";\n"); +} + +bool dump_cell_expr(FILE *f, std::string indent, RTLIL::Cell *cell) +{ + if (cell->type == "$_INV_") { + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Y"]); + fprintf(f, " = "); + fprintf(f, "~"); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", false); + fprintf(f, ";\n"); + return true; + } + + if (cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") { + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Y"]); + fprintf(f, " = "); + dump_cell_expr_port(f, cell, "A", false); + fprintf(f, " "); + if (cell->type == "$_AND_") + fprintf(f, "&"); + if (cell->type == "$_OR_") + fprintf(f, "|"); + if (cell->type == "$_XOR_") + fprintf(f, "^"); + dump_attributes(f, "", cell->attributes, ' '); + fprintf(f, " "); + dump_cell_expr_port(f, cell, "B", false); + fprintf(f, ";\n"); + return true; + } + + if (cell->type == "$_MUX_") { + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Y"]); + fprintf(f, " = "); + dump_cell_expr_port(f, cell, "S", false); + fprintf(f, " ? "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", false); + fprintf(f, " : "); + dump_cell_expr_port(f, cell, "A", false); + fprintf(f, ";\n"); + return true; + } + + if (cell->type.substr(0, 6) == "$_DFF_") + { + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->connections["\\Q"], reg_name); + + if (!out_is_reg_wire) + fprintf(f, "%s" "reg %s;\n", indent.c_str(), reg_name.c_str()); + + dump_attributes(f, indent, cell->attributes); + fprintf(f, "%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg"); + dump_sigspec(f, cell->connections["\\C"]); + if (cell->type[7] != '_') { + fprintf(f, " or %sedge ", cell->type[7] == 'P' ? "pos" : "neg"); + dump_sigspec(f, cell->connections["\\R"]); + } + fprintf(f, ")\n"); + + if (cell->type[7] != '_') { + fprintf(f, "%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!"); + dump_sigspec(f, cell->connections["\\R"]); + fprintf(f, ")\n"); + fprintf(f, "%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]); + fprintf(f, "%s" " else\n", indent.c_str()); + } + + fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str()); + dump_cell_expr_port(f, cell, "D", false); + fprintf(f, ";\n"); + + if (!out_is_reg_wire) { + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Q"]); + fprintf(f, " = %s;\n", reg_name.c_str()); + } + + return true; + } + +#define HANDLE_UNIOP(_type, _operator) \ + if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; } +#define HANDLE_BINOP(_type, _operator) \ + if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; } + + HANDLE_UNIOP("$not", "~") + HANDLE_UNIOP("$pos", "+") + HANDLE_UNIOP("$neg", "-") + + HANDLE_BINOP("$and", "&") + HANDLE_BINOP("$or", "|") + HANDLE_BINOP("$xor", "^") + HANDLE_BINOP("$xnor", "~^") + + HANDLE_UNIOP("$reduce_and", "&") + HANDLE_UNIOP("$reduce_or", "|") + HANDLE_UNIOP("$reduce_xor", "^") + HANDLE_UNIOP("$reduce_xnor", "~^") + HANDLE_UNIOP("$reduce_bool", "|") + + HANDLE_BINOP("$shl", "<<") + HANDLE_BINOP("$shr", ">>") + HANDLE_BINOP("$sshl", "<<<") + HANDLE_BINOP("$sshr", ">>>") + + HANDLE_BINOP("$lt", "<") + HANDLE_BINOP("$le", "<=") + HANDLE_BINOP("$eq", "==") + HANDLE_BINOP("$ne", "!=") + HANDLE_BINOP("$ge", ">=") + HANDLE_BINOP("$gt", ">") + + HANDLE_BINOP("$add", "+") + HANDLE_BINOP("$sub", "-") + HANDLE_BINOP("$mul", "*") + HANDLE_BINOP("$div", "/") + HANDLE_BINOP("$mod", "%") + HANDLE_BINOP("$pow", "**") + + HANDLE_UNIOP("$logic_not", "!") + HANDLE_BINOP("$logic_and", "&&") + HANDLE_BINOP("$logic_or", "||") + +#undef HANDLE_UNIOP +#undef HANDLE_BINOP + + if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$pmux_safe") + { + int width = cell->parameters["\\WIDTH"].as_int(); + int s_width = cell->connections["\\S"].width; + std::string reg_name = cellname(cell); + fprintf(f, "%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str()); + + dump_attributes(f, indent, cell->attributes); + if (!noattr) + fprintf(f, "%s" "(* parallel_case *)\n", indent.c_str()); + fprintf(f, "%s" "always @*\n", indent.c_str()); + fprintf(f, "%s" " casez (", indent.c_str()); + dump_sigspec(f, cell->connections["\\S"]); + fprintf(f, noattr ? ") // synopsys parallel_case\n" : ")\n"); + + for (int i = 0; i < s_width; i++) + { + fprintf(f, "%s" " %d'b", indent.c_str(), s_width); + + for (int j = s_width-1; j >= 0; j--) + fprintf(f, "%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?'); + + fprintf(f, ":\n"); + fprintf(f, "%s" " %s = ", indent.c_str(), reg_name.c_str()); + + RTLIL::SigSpec s = cell->connections["\\B"].extract(i * width, width); + dump_sigspec(f, s); + fprintf(f, ";\n"); + } + + fprintf(f, "%s" " default:\n", indent.c_str()); + fprintf(f, "%s" " %s = ", indent.c_str(), reg_name.c_str()); + dump_sigspec(f, cell->connections["\\A"]); + fprintf(f, ";\n"); + + fprintf(f, "%s" " endcase\n", indent.c_str()); + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Y"]); + fprintf(f, " = %s;\n", reg_name.c_str()); + return true; + } + + if (cell->type == "$dff" || cell->type == "$adff") + { + RTLIL::SigSpec sig_clk, sig_arst, val_arst; + bool pol_clk, pol_arst = false; + + sig_clk = cell->connections["\\CLK"]; + pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool(); + + if (cell->type == "$adff") { + sig_arst = cell->connections["\\ARST"]; + pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool(); + val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]); + } + + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->connections["\\Q"], reg_name); + + if (!out_is_reg_wire) + fprintf(f, "%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str()); + + fprintf(f, "%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); + dump_sigspec(f, sig_clk); + if (cell->type == "$adff") { + fprintf(f, " or %sedge ", pol_arst ? "pos" : "neg"); + dump_sigspec(f, sig_arst); + } + fprintf(f, ")\n"); + + if (cell->type == "$adff") { + fprintf(f, "%s" " if (%s", indent.c_str(), pol_arst ? "" : "!"); + dump_sigspec(f, sig_arst); + fprintf(f, ")\n"); + fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str()); + dump_sigspec(f, val_arst); + fprintf(f, ";\n"); + fprintf(f, "%s" " else\n", indent.c_str()); + } + + fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str()); + dump_cell_expr_port(f, cell, "D", false); + fprintf(f, ";\n"); + + if (!out_is_reg_wire) { + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->connections["\\Q"]); + fprintf(f, " = %s;\n", reg_name.c_str()); + } + + return true; + } + + // FIXME: $memrd, $memwr, $mem, $fsm + + return false; +} + +void dump_cell(FILE *f, std::string indent, RTLIL::Cell *cell) +{ + if (cell->type[0] == '$' && !noexpr) { + if (dump_cell_expr(f, indent, cell)) + return; + } + + dump_attributes(f, indent, cell->attributes); + fprintf(f, "%s" "%s", indent.c_str(), id(cell->type, false).c_str()); + + if (cell->parameters.size() > 0) { + fprintf(f, " #("); + for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) { + if (it != cell->parameters.begin()) + fprintf(f, ","); + fprintf(f, "\n%s .%s(", indent.c_str(), id(it->first).c_str()); + dump_const(f, it->second); + fprintf(f, ")"); + } + fprintf(f, "\n%s" ")", indent.c_str()); + } + + std::string cell_name = cellname(cell); + if (cell_name != id(cell->name)) + fprintf(f, " %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str()); + else + fprintf(f, " %s (", cell_name.c_str()); + + bool first_arg = true; + std::set numbered_ports; + for (int i = 1; true; i++) { + char str[16]; + snprintf(str, 16, "$%d", i); + for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) { + if (it->first != str) + continue; + if (!first_arg) + fprintf(f, ","); + first_arg = false; + fprintf(f, "\n%s ", indent.c_str()); + dump_sigspec(f, it->second); + numbered_ports.insert(it->first); + goto found_numbered_port; + } + break; + found_numbered_port:; + } + for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) { + if (numbered_ports.count(it->first)) + continue; + if (!first_arg) + fprintf(f, ","); + first_arg = false; + fprintf(f, "\n%s .%s(", indent.c_str(), id(it->first).c_str()); + if (it->second.width > 0) + dump_sigspec(f, it->second); + fprintf(f, ")"); + } + fprintf(f, "\n%s" ");\n", indent.c_str()); +} + +void dump_conn(FILE *f, std::string indent, RTLIL::SigSpec &left, RTLIL::SigSpec &right) +{ + fprintf(f, "%s" "assign ", indent.c_str()); + dump_sigspec(f, left); + fprintf(f, " = "); + dump_sigspec(f, right); + fprintf(f, ";\n"); +} + +void dump_proc_switch(FILE *f, std::string indent, RTLIL::SwitchRule *sw); + +void dump_case_body(FILE *f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) +{ + int number_of_stmts = cs->switches.size() + cs->actions.size(); + + if (!omit_trailing_begin && number_of_stmts >= 2) + fprintf(f, "%s" "begin\n", indent.c_str()); + + for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) { + if (it->first.width == 0) + continue; + fprintf(f, "%s ", indent.c_str()); + dump_sigspec(f, it->first); + fprintf(f, " = "); + dump_sigspec(f, it->second); + fprintf(f, ";\n"); + } + + for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + dump_proc_switch(f, indent + " ", *it); + + if (!omit_trailing_begin && number_of_stmts == 0) + fprintf(f, "%s /* empty */;\n", indent.c_str()); + + if (omit_trailing_begin || number_of_stmts >= 2) + fprintf(f, "%s" "end\n", indent.c_str()); +} + +void dump_proc_switch(FILE *f, std::string indent, RTLIL::SwitchRule *sw) +{ + if (sw->signal.width == 0) { + fprintf(f, "%s" "begin\n", indent.c_str()); + for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) { + if ((*it)->compare.size() == 0) + dump_case_body(f, indent + " ", *it); + } + fprintf(f, "%s" "end\n", indent.c_str()); + return; + } + + fprintf(f, "%s" "casez (", indent.c_str()); + dump_sigspec(f, sw->signal); + fprintf(f, ")\n"); + + for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) { + fprintf(f, "%s ", indent.c_str()); + if ((*it)->compare.size() == 0) + fprintf(f, "default"); + else { + for (size_t i = 0; i < (*it)->compare.size(); i++) { + if (i > 0) + fprintf(f, ", "); + dump_sigspec(f, (*it)->compare[i]); + } + } + fprintf(f, ":\n"); + dump_case_body(f, indent + " ", *it); + } + + fprintf(f, "%s" "endcase\n", indent.c_str()); +} + +void case_body_find_regs(RTLIL::CaseRule *cs) +{ + for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) + case_body_find_regs(*it2); + + for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) { + for (size_t i = 0; i < it->first.chunks.size(); i++) + if (it->first.chunks[i].wire) + reg_wires.insert(it->first.chunks[i].wire->name); + } +} + +void dump_process(FILE *f, std::string indent, RTLIL::Process *proc, bool find_regs = false) +{ + if (find_regs) { + case_body_find_regs(&proc->root_case); + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++) + for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) { + for (size_t i = 0; i < it2->first.chunks.size(); i++) + if (it2->first.chunks[i].wire) + reg_wires.insert(it2->first.chunks[i].wire->name); + } + return; + } + + fprintf(f, "%s" "always @* begin\n", indent.c_str()); + dump_case_body(f, indent, &proc->root_case, true); + + std::string backup_indent = indent; + + for (size_t i = 0; i < proc->syncs.size(); i++) + { + RTLIL::SyncRule *sync = proc->syncs[i]; + indent = backup_indent; + + if (sync->type == RTLIL::STa) { + fprintf(f, "%s" "always @* begin\n", indent.c_str()); + } else { + fprintf(f, "%s" "always @(", indent.c_str()); + if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1) + fprintf(f, "posedge "); + if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0) + fprintf(f, "negedge "); + dump_sigspec(f, sync->signal); + fprintf(f, ") begin\n"); + } + std::string ends = indent + "end\n"; + indent += " "; + + if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) { + fprintf(f, "%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : ""); + dump_sigspec(f, sync->signal); + fprintf(f, ") begin\n"); + ends = indent + "end\n" + ends; + indent += " "; + } + + if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) { + for (size_t j = 0; j < proc->syncs.size(); j++) { + RTLIL::SyncRule *sync2 = proc->syncs[j]; + if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) { + fprintf(f, "%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : ""); + dump_sigspec(f, sync2->signal); + fprintf(f, ") begin\n"); + ends = indent + "end\n" + ends; + indent += " "; + } + } + } + + for (auto it = sync->actions.begin(); it != sync->actions.end(); it++) { + if (it->first.width == 0) + continue; + fprintf(f, "%s ", indent.c_str()); + dump_sigspec(f, it->first); + fprintf(f, " <= "); + dump_sigspec(f, it->second); + fprintf(f, ";\n"); + } + + fprintf(f, "%s", ends.c_str()); + } +} + +void dump_module(FILE *f, std::string indent, RTLIL::Module *module) +{ + reg_wires.clear(); + reset_auto_counter(module); + active_module = module; + + for (auto it = module->processes.begin(); it != module->processes.end(); it++) + dump_process(f, indent + " ", it->second, true); + + if (!noexpr) + { + std::set> reg_bits; + for (auto &it : module->cells) + { + RTLIL::Cell *cell = it.second; + if (!reg_ct.cell_known(cell->type) || cell->connections.count("\\Q") == 0) + continue; + + RTLIL::SigSpec sig = cell->connections["\\Q"]; + sig.optimize(); + + if (sig.chunks.size() == 1 && sig.chunks[0].wire) + for (int i = 0; i < sig.chunks[0].width; i++) + reg_bits.insert(std::pair(sig.chunks[0].wire, sig.chunks[0].offset+i)); + } + for (auto &it : module->wires) + { + RTLIL::Wire *wire = it.second; + for (int i = 0; i < wire->width; i++) + if (reg_bits.count(std::pair(wire, i)) == 0) + goto this_wire_aint_reg; + reg_wires.insert(wire->name); + this_wire_aint_reg:; + } + } + + dump_attributes(f, indent, module->attributes); + fprintf(f, "%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); + bool keep_running = true; + for (int port_id = 1; keep_running; port_id++) { + keep_running = false; + for (auto it = module->wires.begin(); it != module->wires.end(); it++) { + RTLIL::Wire *wire = it->second; + if (wire->port_id == port_id) { + if (port_id != 1) + fprintf(f, ", "); + fprintf(f, "%s", id(wire->name).c_str()); + keep_running = true; + continue; + } + } + } + fprintf(f, ");\n"); + + for (auto it = module->wires.begin(); it != module->wires.end(); it++) + dump_wire(f, indent + " ", it->second); + + for (auto it = module->memories.begin(); it != module->memories.end(); it++) + dump_memory(f, indent + " ", it->second); + + for (auto it = module->cells.begin(); it != module->cells.end(); it++) + dump_cell(f, indent + " ", it->second); + + for (auto it = module->processes.begin(); it != module->processes.end(); it++) + dump_process(f, indent + " ", it->second); + + for (auto it = module->connections.begin(); it != module->connections.end(); it++) + dump_conn(f, indent + " ", it->first, it->second); + + fprintf(f, "%s" "endmodule\n", indent.c_str()); + active_module = NULL; +} + +} /* namespace */ + +struct VerilogBackend : public Backend { + VerilogBackend() : Backend("verilog") { } + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + log_header("Executing Verilog backend.\n"); + + norename = false; + noattr = false; + attr2comment = false; + noexpr = false; + + reg_ct.clear(); + reg_ct.setup_stdcells_mem(); + reg_ct.cell_types.insert("$dff"); + reg_ct.cell_types.insert("$adff"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-norename") { + norename = true; + continue; + } + if (arg == "-noattr") { + noattr = true; + continue; + } + if (arg == "-attr2comment") { + attr2comment = true; + continue; + } + if (arg == "-noexpr") { + noexpr = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + for (auto it = design->modules.begin(); it != design->modules.end(); it++) { + log("Dumping module `%s'.\n", it->first.c_str()); + if (it != design->modules.begin()) + fprintf(f, "\n"); + dump_module(f, "", it->second); + } + + reg_ct.clear(); + } +} VerilogBackend; + diff --git a/backends/verilog/verilog_backend.h b/backends/verilog/verilog_backend.h new file mode 100644 index 000000000..c40830ef2 --- /dev/null +++ b/backends/verilog/verilog_backend.h @@ -0,0 +1,39 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A simple and straightforward verilog backend. + * + * Note that RTLIL processes can't always be mapped easily to a Verilog + * process. Therefore this frontend should only be used to export a + * Verilog netlist (i.e. after the "proc" pass has converted all processes + * to logic networks and registers). + * + */ + +#ifndef VERILOG_BACKEND_H +#define VERILOG_BACKEND_H + +#include "kernel/rtlil.h" +#include + +namespace VERILOG_BACKEND { + void verilog_backend(FILE *f, std::vector args, RTLIL::Design *design); +} + +#endif diff --git a/bigint/.gitignore b/bigint/.gitignore new file mode 100644 index 000000000..4467edcf5 --- /dev/null +++ b/bigint/.gitignore @@ -0,0 +1,6 @@ +*.o +sample +testsuite +testsuite.expected +testsuite.out +testsuite.err diff --git a/bigint/BigInteger.cc b/bigint/BigInteger.cc new file mode 100644 index 000000000..3b23aa1e7 --- /dev/null +++ b/bigint/BigInteger.cc @@ -0,0 +1,405 @@ +#include "BigInteger.hh" + +void BigInteger::operator =(const BigInteger &x) { + // Calls like a = a have no effect + if (this == &x) + return; + // Copy sign + sign = x.sign; + // Copy the rest + mag = x.mag; +} + +BigInteger::BigInteger(const Blk *b, Index blen, Sign s) : mag(b, blen) { + switch (s) { + case zero: + if (!mag.isZero()) + throw "BigInteger::BigInteger(const Blk *, Index, Sign): Cannot use a sign of zero with a nonzero magnitude"; + sign = zero; + break; + case positive: + case negative: + // If the magnitude is zero, force the sign to zero. + sign = mag.isZero() ? zero : s; + break; + default: + /* g++ seems to be optimizing out this case on the assumption + * that the sign is a valid member of the enumeration. Oh well. */ + throw "BigInteger::BigInteger(const Blk *, Index, Sign): Invalid sign"; + } +} + +BigInteger::BigInteger(const BigUnsigned &x, Sign s) : mag(x) { + switch (s) { + case zero: + if (!mag.isZero()) + throw "BigInteger::BigInteger(const BigUnsigned &, Sign): Cannot use a sign of zero with a nonzero magnitude"; + sign = zero; + break; + case positive: + case negative: + // If the magnitude is zero, force the sign to zero. + sign = mag.isZero() ? zero : s; + break; + default: + /* g++ seems to be optimizing out this case on the assumption + * that the sign is a valid member of the enumeration. Oh well. */ + throw "BigInteger::BigInteger(const BigUnsigned &, Sign): Invalid sign"; + } +} + +/* CONSTRUCTION FROM PRIMITIVE INTEGERS + * Same idea as in BigUnsigned.cc, except that negative input results in a + * negative BigInteger instead of an exception. */ + +// Done longhand to let us use initialization. +BigInteger::BigInteger(unsigned long x) : mag(x) { sign = mag.isZero() ? zero : positive; } +BigInteger::BigInteger(unsigned int x) : mag(x) { sign = mag.isZero() ? zero : positive; } +BigInteger::BigInteger(unsigned short x) : mag(x) { sign = mag.isZero() ? zero : positive; } + +// For signed input, determine the desired magnitude and sign separately. + +namespace { + template + BigInteger::Blk magOf(X x) { + /* UX(...) cast needed to stop short(-2^15), which negates to + * itself, from sign-extending in the conversion to Blk. */ + return BigInteger::Blk(x < 0 ? UX(-x) : x); + } + template + BigInteger::Sign signOf(X x) { + return (x == 0) ? BigInteger::zero + : (x > 0) ? BigInteger::positive + : BigInteger::negative; + } +} + +BigInteger::BigInteger(long x) : sign(signOf(x)), mag(magOf(x)) {} +BigInteger::BigInteger(int x) : sign(signOf(x)), mag(magOf(x)) {} +BigInteger::BigInteger(short x) : sign(signOf(x)), mag(magOf(x)) {} + +// CONVERSION TO PRIMITIVE INTEGERS + +/* Reuse BigUnsigned's conversion to an unsigned primitive integer. + * The friend is a separate function rather than + * BigInteger::convertToUnsignedPrimitive to avoid requiring BigUnsigned to + * declare BigInteger. */ +template +inline X convertBigUnsignedToPrimitiveAccess(const BigUnsigned &a) { + return a.convertToPrimitive(); +} + +template +X BigInteger::convertToUnsignedPrimitive() const { + if (sign == negative) + throw "BigInteger::to: " + "Cannot convert a negative integer to an unsigned type"; + else + return convertBigUnsignedToPrimitiveAccess(mag); +} + +/* Similar to BigUnsigned::convertToPrimitive, but split into two cases for + * nonnegative and negative numbers. */ +template +X BigInteger::convertToSignedPrimitive() const { + if (sign == zero) + return 0; + else if (mag.getLength() == 1) { + // The single block might fit in an X. Try the conversion. + Blk b = mag.getBlock(0); + if (sign == positive) { + X x = X(b); + if (x >= 0 && Blk(x) == b) + return x; + } else { + X x = -X(b); + /* UX(...) needed to avoid rejecting conversion of + * -2^15 to a short. */ + if (x < 0 && Blk(UX(-x)) == b) + return x; + } + // Otherwise fall through. + } + throw "BigInteger::to: " + "Value is too big to fit in the requested type"; +} + +unsigned long BigInteger::toUnsignedLong () const { return convertToUnsignedPrimitive (); } +unsigned int BigInteger::toUnsignedInt () const { return convertToUnsignedPrimitive (); } +unsigned short BigInteger::toUnsignedShort() const { return convertToUnsignedPrimitive (); } +long BigInteger::toLong () const { return convertToSignedPrimitive (); } +int BigInteger::toInt () const { return convertToSignedPrimitive (); } +short BigInteger::toShort () const { return convertToSignedPrimitive (); } + +// COMPARISON +BigInteger::CmpRes BigInteger::compareTo(const BigInteger &x) const { + // A greater sign implies a greater number + if (sign < x.sign) + return less; + else if (sign > x.sign) + return greater; + else switch (sign) { + // If the signs are the same... + case zero: + return equal; // Two zeros are equal + case positive: + // Compare the magnitudes + return mag.compareTo(x.mag); + case negative: + // Compare the magnitudes, but return the opposite result + return CmpRes(-mag.compareTo(x.mag)); + default: + throw "BigInteger internal error"; + } +} + +/* COPY-LESS OPERATIONS + * These do some messing around to determine the sign of the result, + * then call one of BigUnsigned's copy-less operations. */ + +// See remarks about aliased calls in BigUnsigned.cc . +#define DTRT_ALIASED(cond, op) \ + if (cond) { \ + BigInteger tmpThis; \ + tmpThis.op; \ + *this = tmpThis; \ + return; \ + } + +void BigInteger::add(const BigInteger &a, const BigInteger &b) { + DTRT_ALIASED(this == &a || this == &b, add(a, b)); + // If one argument is zero, copy the other. + if (a.sign == zero) + operator =(b); + else if (b.sign == zero) + operator =(a); + // If the arguments have the same sign, take the + // common sign and add their magnitudes. + else if (a.sign == b.sign) { + sign = a.sign; + mag.add(a.mag, b.mag); + } else { + // Otherwise, their magnitudes must be compared. + switch (a.mag.compareTo(b.mag)) { + case equal: + // If their magnitudes are the same, copy zero. + mag = 0; + sign = zero; + break; + // Otherwise, take the sign of the greater, and subtract + // the lesser magnitude from the greater magnitude. + case greater: + sign = a.sign; + mag.subtract(a.mag, b.mag); + break; + case less: + sign = b.sign; + mag.subtract(b.mag, a.mag); + break; + } + } +} + +void BigInteger::subtract(const BigInteger &a, const BigInteger &b) { + // Notice that this routine is identical to BigInteger::add, + // if one replaces b.sign by its opposite. + DTRT_ALIASED(this == &a || this == &b, subtract(a, b)); + // If a is zero, copy b and flip its sign. If b is zero, copy a. + if (a.sign == zero) { + mag = b.mag; + // Take the negative of _b_'s, sign, not ours. + // Bug pointed out by Sam Larkin on 2005.03.30. + sign = Sign(-b.sign); + } else if (b.sign == zero) + operator =(a); + // If their signs differ, take a.sign and add the magnitudes. + else if (a.sign != b.sign) { + sign = a.sign; + mag.add(a.mag, b.mag); + } else { + // Otherwise, their magnitudes must be compared. + switch (a.mag.compareTo(b.mag)) { + // If their magnitudes are the same, copy zero. + case equal: + mag = 0; + sign = zero; + break; + // If a's magnitude is greater, take a.sign and + // subtract a from b. + case greater: + sign = a.sign; + mag.subtract(a.mag, b.mag); + break; + // If b's magnitude is greater, take the opposite + // of b.sign and subtract b from a. + case less: + sign = Sign(-b.sign); + mag.subtract(b.mag, a.mag); + break; + } + } +} + +void BigInteger::multiply(const BigInteger &a, const BigInteger &b) { + DTRT_ALIASED(this == &a || this == &b, multiply(a, b)); + // If one object is zero, copy zero and return. + if (a.sign == zero || b.sign == zero) { + sign = zero; + mag = 0; + return; + } + // If the signs of the arguments are the same, the result + // is positive, otherwise it is negative. + sign = (a.sign == b.sign) ? positive : negative; + // Multiply the magnitudes. + mag.multiply(a.mag, b.mag); +} + +/* + * DIVISION WITH REMAINDER + * Please read the comments before the definition of + * `BigUnsigned::divideWithRemainder' in `BigUnsigned.cc' for lots of + * information you should know before reading this function. + * + * Following Knuth, I decree that x / y is to be + * 0 if y==0 and floor(real-number x / y) if y!=0. + * Then x % y shall be x - y*(integer x / y). + * + * Note that x = y * (x / y) + (x % y) always holds. + * In addition, (x % y) is from 0 to y - 1 if y > 0, + * and from -(|y| - 1) to 0 if y < 0. (x % y) = x if y = 0. + * + * Examples: (q = a / b, r = a % b) + * a b q r + * === === === === + * 4 3 1 1 + * -4 3 -2 2 + * 4 -3 -2 -2 + * -4 -3 1 -1 + */ +void BigInteger::divideWithRemainder(const BigInteger &b, BigInteger &q) { + // Defend against aliased calls; + // same idea as in BigUnsigned::divideWithRemainder . + if (this == &q) + throw "BigInteger::divideWithRemainder: Cannot write quotient and remainder into the same variable"; + if (this == &b || &q == &b) { + BigInteger tmpB(b); + divideWithRemainder(tmpB, q); + return; + } + + // Division by zero gives quotient 0 and remainder *this + if (b.sign == zero) { + q.mag = 0; + q.sign = zero; + return; + } + // 0 / b gives quotient 0 and remainder 0 + if (sign == zero) { + q.mag = 0; + q.sign = zero; + return; + } + + // Here *this != 0, b != 0. + + // Do the operands have the same sign? + if (sign == b.sign) { + // Yes: easy case. Quotient is zero or positive. + q.sign = positive; + } else { + // No: harder case. Quotient is negative. + q.sign = negative; + // Decrease the magnitude of the dividend by one. + mag--; + /* + * We tinker with the dividend before and with the + * quotient and remainder after so that the result + * comes out right. To see why it works, consider the following + * list of examples, where A is the magnitude-decreased + * a, Q and R are the results of BigUnsigned division + * with remainder on A and |b|, and q and r are the + * final results we want: + * + * a A b Q R q r + * -3 -2 3 0 2 -1 0 + * -4 -3 3 1 0 -2 2 + * -5 -4 3 1 1 -2 1 + * -6 -5 3 1 2 -2 0 + * + * It appears that we need a total of 3 corrections: + * Decrease the magnitude of a to get A. Increase the + * magnitude of Q to get q (and make it negative). + * Find r = (b - 1) - R and give it the desired sign. + */ + } + + // Divide the magnitudes. + mag.divideWithRemainder(b.mag, q.mag); + + if (sign != b.sign) { + // More for the harder case (as described): + // Increase the magnitude of the quotient by one. + q.mag++; + // Modify the remainder. + mag.subtract(b.mag, mag); + mag--; + } + + // Sign of the remainder is always the sign of the divisor b. + sign = b.sign; + + // Set signs to zero as necessary. (Thanks David Allen!) + if (mag.isZero()) + sign = zero; + if (q.mag.isZero()) + q.sign = zero; + + // WHEW!!! +} + +// Negation +void BigInteger::negate(const BigInteger &a) { + DTRT_ALIASED(this == &a, negate(a)); + // Copy a's magnitude + mag = a.mag; + // Copy the opposite of a.sign + sign = Sign(-a.sign); +} + +// INCREMENT/DECREMENT OPERATORS + +// Prefix increment +void BigInteger::operator ++() { + if (sign == negative) { + mag--; + if (mag == 0) + sign = zero; + } else { + mag++; + sign = positive; // if not already + } +} + +// Postfix increment: same as prefix +void BigInteger::operator ++(int) { + operator ++(); +} + +// Prefix decrement +void BigInteger::operator --() { + if (sign == positive) { + mag--; + if (mag == 0) + sign = zero; + } else { + mag++; + sign = negative; + } +} + +// Postfix decrement: same as prefix +void BigInteger::operator --(int) { + operator --(); +} + diff --git a/bigint/BigInteger.hh b/bigint/BigInteger.hh new file mode 100644 index 000000000..cf6e91056 --- /dev/null +++ b/bigint/BigInteger.hh @@ -0,0 +1,215 @@ +#ifndef BIGINTEGER_H +#define BIGINTEGER_H + +#include "BigUnsigned.hh" + +/* A BigInteger object represents a signed integer of size limited only by + * available memory. BigUnsigneds support most mathematical operators and can + * be converted to and from most primitive integer types. + * + * A BigInteger is just an aggregate of a BigUnsigned and a sign. (It is no + * longer derived from BigUnsigned because that led to harmful implicit + * conversions.) */ +class BigInteger { + +public: + typedef BigUnsigned::Blk Blk; + typedef BigUnsigned::Index Index; + typedef BigUnsigned::CmpRes CmpRes; + static const CmpRes + less = BigUnsigned::less , + equal = BigUnsigned::equal , + greater = BigUnsigned::greater; + // Enumeration for the sign of a BigInteger. + enum Sign { negative = -1, zero = 0, positive = 1 }; + +protected: + Sign sign; + BigUnsigned mag; + +public: + // Constructs zero. + BigInteger() : sign(zero), mag() {} + + // Copy constructor + BigInteger(const BigInteger &x) : sign(x.sign), mag(x.mag) {}; + + // Assignment operator + void operator=(const BigInteger &x); + + // Constructor that copies from a given array of blocks with a sign. + BigInteger(const Blk *b, Index blen, Sign s); + + // Nonnegative constructor that copies from a given array of blocks. + BigInteger(const Blk *b, Index blen) : mag(b, blen) { + sign = mag.isZero() ? zero : positive; + } + + // Constructor from a BigUnsigned and a sign + BigInteger(const BigUnsigned &x, Sign s); + + // Nonnegative constructor from a BigUnsigned + BigInteger(const BigUnsigned &x) : mag(x) { + sign = mag.isZero() ? zero : positive; + } + + // Constructors from primitive integer types + BigInteger(unsigned long x); + BigInteger( long x); + BigInteger(unsigned int x); + BigInteger( int x); + BigInteger(unsigned short x); + BigInteger( short x); + + /* Converters to primitive integer types + * The implicit conversion operators caused trouble, so these are now + * named. */ + unsigned long toUnsignedLong () const; + long toLong () const; + unsigned int toUnsignedInt () const; + int toInt () const; + unsigned short toUnsignedShort() const; + short toShort () const; +protected: + // Helper + template X convertToUnsignedPrimitive() const; + template X convertToSignedPrimitive() const; +public: + + // ACCESSORS + Sign getSign() const { return sign; } + /* The client can't do any harm by holding a read-only reference to the + * magnitude. */ + const BigUnsigned &getMagnitude() const { return mag; } + + // Some accessors that go through to the magnitude + Index getLength() const { return mag.getLength(); } + Index getCapacity() const { return mag.getCapacity(); } + Blk getBlock(Index i) const { return mag.getBlock(i); } + bool isZero() const { return sign == zero; } // A bit special + + // COMPARISONS + + // Compares this to x like Perl's <=> + CmpRes compareTo(const BigInteger &x) const; + + // Ordinary comparison operators + bool operator ==(const BigInteger &x) const { + return sign == x.sign && mag == x.mag; + } + bool operator !=(const BigInteger &x) const { return !operator ==(x); }; + bool operator < (const BigInteger &x) const { return compareTo(x) == less ; } + bool operator <=(const BigInteger &x) const { return compareTo(x) != greater; } + bool operator >=(const BigInteger &x) const { return compareTo(x) != less ; } + bool operator > (const BigInteger &x) const { return compareTo(x) == greater; } + + // OPERATORS -- See the discussion in BigUnsigned.hh. + void add (const BigInteger &a, const BigInteger &b); + void subtract(const BigInteger &a, const BigInteger &b); + void multiply(const BigInteger &a, const BigInteger &b); + /* See the comment on BigUnsigned::divideWithRemainder. Semantics + * differ from those of primitive integers when negatives and/or zeros + * are involved. */ + void divideWithRemainder(const BigInteger &b, BigInteger &q); + void negate(const BigInteger &a); + + /* Bitwise operators are not provided for BigIntegers. Use + * getMagnitude to get the magnitude and operate on that instead. */ + + BigInteger operator +(const BigInteger &x) const; + BigInteger operator -(const BigInteger &x) const; + BigInteger operator *(const BigInteger &x) const; + BigInteger operator /(const BigInteger &x) const; + BigInteger operator %(const BigInteger &x) const; + BigInteger operator -() const; + + void operator +=(const BigInteger &x); + void operator -=(const BigInteger &x); + void operator *=(const BigInteger &x); + void operator /=(const BigInteger &x); + void operator %=(const BigInteger &x); + void flipSign(); + + // INCREMENT/DECREMENT OPERATORS + void operator ++( ); + void operator ++(int); + void operator --( ); + void operator --(int); +}; + +// NORMAL OPERATORS +/* These create an object to hold the result and invoke + * the appropriate put-here operation on it, passing + * this and x. The new object is then returned. */ +inline BigInteger BigInteger::operator +(const BigInteger &x) const { + BigInteger ans; + ans.add(*this, x); + return ans; +} +inline BigInteger BigInteger::operator -(const BigInteger &x) const { + BigInteger ans; + ans.subtract(*this, x); + return ans; +} +inline BigInteger BigInteger::operator *(const BigInteger &x) const { + BigInteger ans; + ans.multiply(*this, x); + return ans; +} +inline BigInteger BigInteger::operator /(const BigInteger &x) const { + if (x.isZero()) throw "BigInteger::operator /: division by zero"; + BigInteger q, r; + r = *this; + r.divideWithRemainder(x, q); + return q; +} +inline BigInteger BigInteger::operator %(const BigInteger &x) const { + if (x.isZero()) throw "BigInteger::operator %: division by zero"; + BigInteger q, r; + r = *this; + r.divideWithRemainder(x, q); + return r; +} +inline BigInteger BigInteger::operator -() const { + BigInteger ans; + ans.negate(*this); + return ans; +} + +/* + * ASSIGNMENT OPERATORS + * + * Now the responsibility for making a temporary copy if necessary + * belongs to the put-here operations. See Assignment Operators in + * BigUnsigned.hh. + */ +inline void BigInteger::operator +=(const BigInteger &x) { + add(*this, x); +} +inline void BigInteger::operator -=(const BigInteger &x) { + subtract(*this, x); +} +inline void BigInteger::operator *=(const BigInteger &x) { + multiply(*this, x); +} +inline void BigInteger::operator /=(const BigInteger &x) { + if (x.isZero()) throw "BigInteger::operator /=: division by zero"; + /* The following technique is slightly faster than copying *this first + * when x is large. */ + BigInteger q; + divideWithRemainder(x, q); + // *this contains the remainder, but we overwrite it with the quotient. + *this = q; +} +inline void BigInteger::operator %=(const BigInteger &x) { + if (x.isZero()) throw "BigInteger::operator %=: division by zero"; + BigInteger q; + // Mods *this by x. Don't care about quotient left in q. + divideWithRemainder(x, q); +} +// This one is trivial +inline void BigInteger::flipSign() { + sign = Sign(-sign); +} + +#endif diff --git a/bigint/BigIntegerAlgorithms.cc b/bigint/BigIntegerAlgorithms.cc new file mode 100644 index 000000000..7edebda76 --- /dev/null +++ b/bigint/BigIntegerAlgorithms.cc @@ -0,0 +1,70 @@ +#include "BigIntegerAlgorithms.hh" + +BigUnsigned gcd(BigUnsigned a, BigUnsigned b) { + BigUnsigned trash; + // Neat in-place alternating technique. + for (;;) { + if (b.isZero()) + return a; + a.divideWithRemainder(b, trash); + if (a.isZero()) + return b; + b.divideWithRemainder(a, trash); + } +} + +void extendedEuclidean(BigInteger m, BigInteger n, + BigInteger &g, BigInteger &r, BigInteger &s) { + if (&g == &r || &g == &s || &r == &s) + throw "BigInteger extendedEuclidean: Outputs are aliased"; + BigInteger r1(1), s1(0), r2(0), s2(1), q; + /* Invariants: + * r1*m(orig) + s1*n(orig) == m(current) + * r2*m(orig) + s2*n(orig) == n(current) */ + for (;;) { + if (n.isZero()) { + r = r1; s = s1; g = m; + return; + } + // Subtract q times the second invariant from the first invariant. + m.divideWithRemainder(n, q); + r1 -= q*r2; s1 -= q*s2; + + if (m.isZero()) { + r = r2; s = s2; g = n; + return; + } + // Subtract q times the first invariant from the second invariant. + n.divideWithRemainder(m, q); + r2 -= q*r1; s2 -= q*s1; + } +} + +BigUnsigned modinv(const BigInteger &x, const BigUnsigned &n) { + BigInteger g, r, s; + extendedEuclidean(x, n, g, r, s); + if (g == 1) + // r*x + s*n == 1, so r*x === 1 (mod n), so r is the answer. + return (r % n).getMagnitude(); // (r % n) will be nonnegative + else + throw "BigInteger modinv: x and n have a common factor"; +} + +BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent, + const BigUnsigned &modulus) { + BigUnsigned ans = 1, base2 = (base % modulus).getMagnitude(); + BigUnsigned::Index i = exponent.bitLength(); + // For each bit of the exponent, most to least significant... + while (i > 0) { + i--; + // Square. + ans *= ans; + ans %= modulus; + // And multiply if the bit is a 1. + if (exponent.getBit(i)) { + ans *= base2; + ans %= modulus; + } + } + return ans; +} diff --git a/bigint/BigIntegerAlgorithms.hh b/bigint/BigIntegerAlgorithms.hh new file mode 100644 index 000000000..b1dd94322 --- /dev/null +++ b/bigint/BigIntegerAlgorithms.hh @@ -0,0 +1,25 @@ +#ifndef BIGINTEGERALGORITHMS_H +#define BIGINTEGERALGORITHMS_H + +#include "BigInteger.hh" + +/* Some mathematical algorithms for big integers. + * This code is new and, as such, experimental. */ + +// Returns the greatest common divisor of a and b. +BigUnsigned gcd(BigUnsigned a, BigUnsigned b); + +/* Extended Euclidean algorithm. + * Given m and n, finds gcd g and numbers r, s such that r*m + s*n == g. */ +void extendedEuclidean(BigInteger m, BigInteger n, + BigInteger &g, BigInteger &r, BigInteger &s); + +/* Returns the multiplicative inverse of x modulo n, or throws an exception if + * they have a common factor. */ +BigUnsigned modinv(const BigInteger &x, const BigUnsigned &n); + +// Returns (base ^ exponent) % modulus. +BigUnsigned modexp(const BigInteger &base, const BigUnsigned &exponent, + const BigUnsigned &modulus); + +#endif diff --git a/bigint/BigIntegerLibrary.hh b/bigint/BigIntegerLibrary.hh new file mode 100644 index 000000000..2a0ebee6a --- /dev/null +++ b/bigint/BigIntegerLibrary.hh @@ -0,0 +1,8 @@ +// This header file includes all of the library header files. + +#include "NumberlikeArray.hh" +#include "BigUnsigned.hh" +#include "BigInteger.hh" +#include "BigIntegerAlgorithms.hh" +#include "BigUnsignedInABase.hh" +#include "BigIntegerUtils.hh" diff --git a/bigint/BigIntegerUtils.cc b/bigint/BigIntegerUtils.cc new file mode 100644 index 000000000..44073af65 --- /dev/null +++ b/bigint/BigIntegerUtils.cc @@ -0,0 +1,50 @@ +#include "BigIntegerUtils.hh" +#include "BigUnsignedInABase.hh" + +std::string bigUnsignedToString(const BigUnsigned &x) { + return std::string(BigUnsignedInABase(x, 10)); +} + +std::string bigIntegerToString(const BigInteger &x) { + return (x.getSign() == BigInteger::negative) + ? (std::string("-") + bigUnsignedToString(x.getMagnitude())) + : (bigUnsignedToString(x.getMagnitude())); +} + +BigUnsigned stringToBigUnsigned(const std::string &s) { + return BigUnsigned(BigUnsignedInABase(s, 10)); +} + +BigInteger stringToBigInteger(const std::string &s) { + // Recognize a sign followed by a BigUnsigned. + return (s[0] == '-') ? BigInteger(stringToBigUnsigned(s.substr(1, s.length() - 1)), BigInteger::negative) + : (s[0] == '+') ? BigInteger(stringToBigUnsigned(s.substr(1, s.length() - 1))) + : BigInteger(stringToBigUnsigned(s)); +} + +std::ostream &operator <<(std::ostream &os, const BigUnsigned &x) { + BigUnsignedInABase::Base base; + long osFlags = os.flags(); + if (osFlags & os.dec) + base = 10; + else if (osFlags & os.hex) { + base = 16; + if (osFlags & os.showbase) + os << "0x"; + } else if (osFlags & os.oct) { + base = 8; + if (osFlags & os.showbase) + os << '0'; + } else + throw "std::ostream << BigUnsigned: Could not determine the desired base from output-stream flags"; + std::string s = std::string(BigUnsignedInABase(x, base)); + os << s; + return os; +} + +std::ostream &operator <<(std::ostream &os, const BigInteger &x) { + if (x.getSign() == BigInteger::negative) + os << '-'; + os << x.getMagnitude(); + return os; +} diff --git a/bigint/BigIntegerUtils.hh b/bigint/BigIntegerUtils.hh new file mode 100644 index 000000000..c815b5d7c --- /dev/null +++ b/bigint/BigIntegerUtils.hh @@ -0,0 +1,72 @@ +#ifndef BIGINTEGERUTILS_H +#define BIGINTEGERUTILS_H + +#include "BigInteger.hh" +#include +#include + +/* This file provides: + * - Convenient std::string <-> BigUnsigned/BigInteger conversion routines + * - std::ostream << operators for BigUnsigned/BigInteger */ + +// std::string conversion routines. Base 10 only. +std::string bigUnsignedToString(const BigUnsigned &x); +std::string bigIntegerToString(const BigInteger &x); +BigUnsigned stringToBigUnsigned(const std::string &s); +BigInteger stringToBigInteger(const std::string &s); + +// Creates a BigInteger from data such as `char's; read below for details. +template +BigInteger dataToBigInteger(const T* data, BigInteger::Index length, BigInteger::Sign sign); + +// Outputs x to os, obeying the flags `dec', `hex', `bin', and `showbase'. +std::ostream &operator <<(std::ostream &os, const BigUnsigned &x); + +// Outputs x to os, obeying the flags `dec', `hex', `bin', and `showbase'. +// My somewhat arbitrary policy: a negative sign comes before a base indicator (like -0xFF). +std::ostream &operator <<(std::ostream &os, const BigInteger &x); + +// BEGIN TEMPLATE DEFINITIONS. + +/* + * Converts binary data to a BigInteger. + * Pass an array `data', its length, and the desired sign. + * + * Elements of `data' may be of any type `T' that has the following + * two properties (this includes almost all integral types): + * + * (1) `sizeof(T)' correctly gives the amount of binary data in one + * value of `T' and is a factor of `sizeof(Blk)'. + * + * (2) When a value of `T' is casted to a `Blk', the low bytes of + * the result contain the desired binary data. + */ +template +BigInteger dataToBigInteger(const T* data, BigInteger::Index length, BigInteger::Sign sign) { + // really ceiling(numBytes / sizeof(BigInteger::Blk)) + unsigned int pieceSizeInBits = 8 * sizeof(T); + unsigned int piecesPerBlock = sizeof(BigInteger::Blk) / sizeof(T); + unsigned int numBlocks = (length + piecesPerBlock - 1) / piecesPerBlock; + + // Allocate our block array + BigInteger::Blk *blocks = new BigInteger::Blk[numBlocks]; + + BigInteger::Index blockNum, pieceNum, pieceNumHere; + + // Convert + for (blockNum = 0, pieceNum = 0; blockNum < numBlocks; blockNum++) { + BigInteger::Blk curBlock = 0; + for (pieceNumHere = 0; pieceNumHere < piecesPerBlock && pieceNum < length; + pieceNumHere++, pieceNum++) + curBlock |= (BigInteger::Blk(data[pieceNum]) << (pieceSizeInBits * pieceNumHere)); + blocks[blockNum] = curBlock; + } + + // Create the BigInteger. + BigInteger x(blocks, numBlocks, sign); + + delete [] blocks; + return x; +} + +#endif diff --git a/bigint/BigUnsigned.cc b/bigint/BigUnsigned.cc new file mode 100644 index 000000000..d7f9889cc --- /dev/null +++ b/bigint/BigUnsigned.cc @@ -0,0 +1,697 @@ +#include "BigUnsigned.hh" + +// Memory management definitions have moved to the bottom of NumberlikeArray.hh. + +// The templates used by these constructors and converters are at the bottom of +// BigUnsigned.hh. + +BigUnsigned::BigUnsigned(unsigned long x) { initFromPrimitive (x); } +BigUnsigned::BigUnsigned(unsigned int x) { initFromPrimitive (x); } +BigUnsigned::BigUnsigned(unsigned short x) { initFromPrimitive (x); } +BigUnsigned::BigUnsigned( long x) { initFromSignedPrimitive(x); } +BigUnsigned::BigUnsigned( int x) { initFromSignedPrimitive(x); } +BigUnsigned::BigUnsigned( short x) { initFromSignedPrimitive(x); } + +unsigned long BigUnsigned::toUnsignedLong () const { return convertToPrimitive (); } +unsigned int BigUnsigned::toUnsignedInt () const { return convertToPrimitive (); } +unsigned short BigUnsigned::toUnsignedShort() const { return convertToPrimitive (); } +long BigUnsigned::toLong () const { return convertToSignedPrimitive< long >(); } +int BigUnsigned::toInt () const { return convertToSignedPrimitive< int >(); } +short BigUnsigned::toShort () const { return convertToSignedPrimitive< short>(); } + +// BIT/BLOCK ACCESSORS + +void BigUnsigned::setBlock(Index i, Blk newBlock) { + if (newBlock == 0) { + if (i < len) { + blk[i] = 0; + zapLeadingZeros(); + } + // If i >= len, no effect. + } else { + if (i >= len) { + // The nonzero block extends the number. + allocateAndCopy(i+1); + // Zero any added blocks that we aren't setting. + for (Index j = len; j < i; j++) + blk[j] = 0; + len = i+1; + } + blk[i] = newBlock; + } +} + +/* Evidently the compiler wants BigUnsigned:: on the return type because, at + * that point, it hasn't yet parsed the BigUnsigned:: on the name to get the + * proper scope. */ +BigUnsigned::Index BigUnsigned::bitLength() const { + if (isZero()) + return 0; + else { + Blk leftmostBlock = getBlock(len - 1); + Index leftmostBlockLen = 0; + while (leftmostBlock != 0) { + leftmostBlock >>= 1; + leftmostBlockLen++; + } + return leftmostBlockLen + (len - 1) * N; + } +} + +void BigUnsigned::setBit(Index bi, bool newBit) { + Index blockI = bi / N; + Blk block = getBlock(blockI), mask = Blk(1) << (bi % N); + block = newBit ? (block | mask) : (block & ~mask); + setBlock(blockI, block); +} + +// COMPARISON +BigUnsigned::CmpRes BigUnsigned::compareTo(const BigUnsigned &x) const { + // A bigger length implies a bigger number. + if (len < x.len) + return less; + else if (len > x.len) + return greater; + else { + // Compare blocks one by one from left to right. + Index i = len; + while (i > 0) { + i--; + if (blk[i] == x.blk[i]) + continue; + else if (blk[i] > x.blk[i]) + return greater; + else + return less; + } + // If no blocks differed, the numbers are equal. + return equal; + } +} + +// COPY-LESS OPERATIONS + +/* + * On most calls to copy-less operations, it's safe to read the inputs little by + * little and write the outputs little by little. However, if one of the + * inputs is coming from the same variable into which the output is to be + * stored (an "aliased" call), we risk overwriting the input before we read it. + * In this case, we first compute the result into a temporary BigUnsigned + * variable and then copy it into the requested output variable *this. + * Each put-here operation uses the DTRT_ALIASED macro (Do The Right Thing on + * aliased calls) to generate code for this check. + * + * I adopted this approach on 2007.02.13 (see Assignment Operators in + * BigUnsigned.hh). Before then, put-here operations rejected aliased calls + * with an exception. I think doing the right thing is better. + * + * Some of the put-here operations can probably handle aliased calls safely + * without the extra copy because (for example) they process blocks strictly + * right-to-left. At some point I might determine which ones don't need the + * copy, but my reasoning would need to be verified very carefully. For now + * I'll leave in the copy. + */ +#define DTRT_ALIASED(cond, op) \ + if (cond) { \ + BigUnsigned tmpThis; \ + tmpThis.op; \ + *this = tmpThis; \ + return; \ + } + + + +void BigUnsigned::add(const BigUnsigned &a, const BigUnsigned &b) { + DTRT_ALIASED(this == &a || this == &b, add(a, b)); + // If one argument is zero, copy the other. + if (a.len == 0) { + operator =(b); + return; + } else if (b.len == 0) { + operator =(a); + return; + } + // Some variables... + // Carries in and out of an addition stage + bool carryIn, carryOut; + Blk temp; + Index i; + // a2 points to the longer input, b2 points to the shorter + const BigUnsigned *a2, *b2; + if (a.len >= b.len) { + a2 = &a; + b2 = &b; + } else { + a2 = &b; + b2 = &a; + } + // Set prelimiary length and make room in this BigUnsigned + len = a2->len + 1; + allocate(len); + // For each block index that is present in both inputs... + for (i = 0, carryIn = false; i < b2->len; i++) { + // Add input blocks + temp = a2->blk[i] + b2->blk[i]; + // If a rollover occurred, the result is less than either input. + // This test is used many times in the BigUnsigned code. + carryOut = (temp < a2->blk[i]); + // If a carry was input, handle it + if (carryIn) { + temp++; + carryOut |= (temp == 0); + } + blk[i] = temp; // Save the addition result + carryIn = carryOut; // Pass the carry along + } + // If there is a carry left over, increase blocks until + // one does not roll over. + for (; i < a2->len && carryIn; i++) { + temp = a2->blk[i] + 1; + carryIn = (temp == 0); + blk[i] = temp; + } + // If the carry was resolved but the larger number + // still has blocks, copy them over. + for (; i < a2->len; i++) + blk[i] = a2->blk[i]; + // Set the extra block if there's still a carry, decrease length otherwise + if (carryIn) + blk[i] = 1; + else + len--; +} + +void BigUnsigned::subtract(const BigUnsigned &a, const BigUnsigned &b) { + DTRT_ALIASED(this == &a || this == &b, subtract(a, b)); + if (b.len == 0) { + // If b is zero, copy a. + operator =(a); + return; + } else if (a.len < b.len) + // If a is shorter than b, the result is negative. + throw "BigUnsigned::subtract: " + "Negative result in unsigned calculation"; + // Some variables... + bool borrowIn, borrowOut; + Blk temp; + Index i; + // Set preliminary length and make room + len = a.len; + allocate(len); + // For each block index that is present in both inputs... + for (i = 0, borrowIn = false; i < b.len; i++) { + temp = a.blk[i] - b.blk[i]; + // If a reverse rollover occurred, + // the result is greater than the block from a. + borrowOut = (temp > a.blk[i]); + // Handle an incoming borrow + if (borrowIn) { + borrowOut |= (temp == 0); + temp--; + } + blk[i] = temp; // Save the subtraction result + borrowIn = borrowOut; // Pass the borrow along + } + // If there is a borrow left over, decrease blocks until + // one does not reverse rollover. + for (; i < a.len && borrowIn; i++) { + borrowIn = (a.blk[i] == 0); + blk[i] = a.blk[i] - 1; + } + /* If there's still a borrow, the result is negative. + * Throw an exception, but zero out this object so as to leave it in a + * predictable state. */ + if (borrowIn) { + len = 0; + throw "BigUnsigned::subtract: Negative result in unsigned calculation"; + } else + // Copy over the rest of the blocks + for (; i < a.len; i++) + blk[i] = a.blk[i]; + // Zap leading zeros + zapLeadingZeros(); +} + +/* + * About the multiplication and division algorithms: + * + * I searched unsucessfully for fast C++ built-in operations like the `b_0' + * and `c_0' Knuth describes in Section 4.3.1 of ``The Art of Computer + * Programming'' (replace `place' by `Blk'): + * + * ``b_0[:] multiplication of a one-place integer by another one-place + * integer, giving a two-place answer; + * + * ``c_0[:] division of a two-place integer by a one-place integer, + * provided that the quotient is a one-place integer, and yielding + * also a one-place remainder.'' + * + * I also missed his note that ``[b]y adjusting the word size, if + * necessary, nearly all computers will have these three operations + * available'', so I gave up on trying to use algorithms similar to his. + * A future version of the library might include such algorithms; I + * would welcome contributions from others for this. + * + * I eventually decided to use bit-shifting algorithms. To multiply `a' + * and `b', we zero out the result. Then, for each `1' bit in `a', we + * shift `b' left the appropriate amount and add it to the result. + * Similarly, to divide `a' by `b', we shift `b' left varying amounts, + * repeatedly trying to subtract it from `a'. When we succeed, we note + * the fact by setting a bit in the quotient. While these algorithms + * have the same O(n^2) time complexity as Knuth's, the ``constant factor'' + * is likely to be larger. + * + * Because I used these algorithms, which require single-block addition + * and subtraction rather than single-block multiplication and division, + * the innermost loops of all four routines are very similar. Study one + * of them and all will become clear. + */ + +/* + * This is a little inline function used by both the multiplication + * routine and the division routine. + * + * `getShiftedBlock' returns the `x'th block of `num << y'. + * `y' may be anything from 0 to N - 1, and `x' may be anything from + * 0 to `num.len'. + * + * Two things contribute to this block: + * + * (1) The `N - y' low bits of `num.blk[x]', shifted `y' bits left. + * + * (2) The `y' high bits of `num.blk[x-1]', shifted `N - y' bits right. + * + * But we must be careful if `x == 0' or `x == num.len', in + * which case we should use 0 instead of (2) or (1), respectively. + * + * If `y == 0', then (2) contributes 0, as it should. However, + * in some computer environments, for a reason I cannot understand, + * `a >> b' means `a >> (b % N)'. This means `num.blk[x-1] >> (N - y)' + * will return `num.blk[x-1]' instead of the desired 0 when `y == 0'; + * the test `y == 0' handles this case specially. + */ +inline BigUnsigned::Blk getShiftedBlock(const BigUnsigned &num, + BigUnsigned::Index x, unsigned int y) { + BigUnsigned::Blk part1 = (x == 0 || y == 0) ? 0 : (num.blk[x - 1] >> (BigUnsigned::N - y)); + BigUnsigned::Blk part2 = (x == num.len) ? 0 : (num.blk[x] << y); + return part1 | part2; +} + +void BigUnsigned::multiply(const BigUnsigned &a, const BigUnsigned &b) { + DTRT_ALIASED(this == &a || this == &b, multiply(a, b)); + // If either a or b is zero, set to zero. + if (a.len == 0 || b.len == 0) { + len = 0; + return; + } + /* + * Overall method: + * + * Set this = 0. + * For each 1-bit of `a' (say the `i2'th bit of block `i'): + * Add `b << (i blocks and i2 bits)' to *this. + */ + // Variables for the calculation + Index i, j, k; + unsigned int i2; + Blk temp; + bool carryIn, carryOut; + // Set preliminary length and make room + len = a.len + b.len; + allocate(len); + // Zero out this object + for (i = 0; i < len; i++) + blk[i] = 0; + // For each block of the first number... + for (i = 0; i < a.len; i++) { + // For each 1-bit of that block... + for (i2 = 0; i2 < N; i2++) { + if ((a.blk[i] & (Blk(1) << i2)) == 0) + continue; + /* + * Add b to this, shifted left i blocks and i2 bits. + * j is the index in b, and k = i + j is the index in this. + * + * `getShiftedBlock', a short inline function defined above, + * is now used for the bit handling. It replaces the more + * complex `bHigh' code, in which each run of the loop dealt + * immediately with the low bits and saved the high bits to + * be picked up next time. The last run of the loop used to + * leave leftover high bits, which were handled separately. + * Instead, this loop runs an additional time with j == b.len. + * These changes were made on 2005.01.11. + */ + for (j = 0, k = i, carryIn = false; j <= b.len; j++, k++) { + /* + * The body of this loop is very similar to the body of the first loop + * in `add', except that this loop does a `+=' instead of a `+'. + */ + temp = blk[k] + getShiftedBlock(b, j, i2); + carryOut = (temp < blk[k]); + if (carryIn) { + temp++; + carryOut |= (temp == 0); + } + blk[k] = temp; + carryIn = carryOut; + } + // No more extra iteration to deal with `bHigh'. + // Roll-over a carry as necessary. + for (; carryIn; k++) { + blk[k]++; + carryIn = (blk[k] == 0); + } + } + } + // Zap possible leading zero + if (blk[len - 1] == 0) + len--; +} + +/* + * DIVISION WITH REMAINDER + * This monstrous function mods *this by the given divisor b while storing the + * quotient in the given object q; at the end, *this contains the remainder. + * The seemingly bizarre pattern of inputs and outputs was chosen so that the + * function copies as little as possible (since it is implemented by repeated + * subtraction of multiples of b from *this). + * + * "modWithQuotient" might be a better name for this function, but I would + * rather not change the name now. + */ +void BigUnsigned::divideWithRemainder(const BigUnsigned &b, BigUnsigned &q) { + /* Defending against aliased calls is more complex than usual because we + * are writing to both *this and q. + * + * It would be silly to try to write quotient and remainder to the + * same variable. Rule that out right away. */ + if (this == &q) + throw "BigUnsigned::divideWithRemainder: Cannot write quotient and remainder into the same variable"; + /* Now *this and q are separate, so the only concern is that b might be + * aliased to one of them. If so, use a temporary copy of b. */ + if (this == &b || &q == &b) { + BigUnsigned tmpB(b); + divideWithRemainder(tmpB, q); + return; + } + + /* + * Knuth's definition of mod (which this function uses) is somewhat + * different from the C++ definition of % in case of division by 0. + * + * We let a / 0 == 0 (it doesn't matter much) and a % 0 == a, no + * exceptions thrown. This allows us to preserve both Knuth's demand + * that a mod 0 == a and the useful property that + * (a / b) * b + (a % b) == a. + */ + if (b.len == 0) { + q.len = 0; + return; + } + + /* + * If *this.len < b.len, then *this < b, and we can be sure that b doesn't go into + * *this at all. The quotient is 0 and *this is already the remainder (so leave it alone). + */ + if (len < b.len) { + q.len = 0; + return; + } + + // At this point we know (*this).len >= b.len > 0. (Whew!) + + /* + * Overall method: + * + * For each appropriate i and i2, decreasing: + * Subtract (b << (i blocks and i2 bits)) from *this, storing the + * result in subtractBuf. + * If the subtraction succeeds with a nonnegative result: + * Turn on bit i2 of block i of the quotient q. + * Copy subtractBuf back into *this. + * Otherwise bit i2 of block i remains off, and *this is unchanged. + * + * Eventually q will contain the entire quotient, and *this will + * be left with the remainder. + * + * subtractBuf[x] corresponds to blk[x], not blk[x+i], since 2005.01.11. + * But on a single iteration, we don't touch the i lowest blocks of blk + * (and don't use those of subtractBuf) because these blocks are + * unaffected by the subtraction: we are subtracting + * (b << (i blocks and i2 bits)), which ends in at least `i' zero + * blocks. */ + // Variables for the calculation + Index i, j, k; + unsigned int i2; + Blk temp; + bool borrowIn, borrowOut; + + /* + * Make sure we have an extra zero block just past the value. + * + * When we attempt a subtraction, we might shift `b' so + * its first block begins a few bits left of the dividend, + * and then we'll try to compare these extra bits with + * a nonexistent block to the left of the dividend. The + * extra zero block ensures sensible behavior; we need + * an extra block in `subtractBuf' for exactly the same reason. + */ + Index origLen = len; // Save real length. + /* To avoid an out-of-bounds access in case of reallocation, allocate + * first and then increment the logical length. */ + allocateAndCopy(len + 1); + len++; + blk[origLen] = 0; // Zero the added block. + + // subtractBuf holds part of the result of a subtraction; see above. + Blk *subtractBuf = new Blk[len]; + + // Set preliminary length for quotient and make room + q.len = origLen - b.len + 1; + q.allocate(q.len); + // Zero out the quotient + for (i = 0; i < q.len; i++) + q.blk[i] = 0; + + // For each possible left-shift of b in blocks... + i = q.len; + while (i > 0) { + i--; + // For each possible left-shift of b in bits... + // (Remember, N is the number of bits in a Blk.) + q.blk[i] = 0; + i2 = N; + while (i2 > 0) { + i2--; + /* + * Subtract b, shifted left i blocks and i2 bits, from *this, + * and store the answer in subtractBuf. In the for loop, `k == i + j'. + * + * Compare this to the middle section of `multiply'. They + * are in many ways analogous. See especially the discussion + * of `getShiftedBlock'. + */ + for (j = 0, k = i, borrowIn = false; j <= b.len; j++, k++) { + temp = blk[k] - getShiftedBlock(b, j, i2); + borrowOut = (temp > blk[k]); + if (borrowIn) { + borrowOut |= (temp == 0); + temp--; + } + // Since 2005.01.11, indices of `subtractBuf' directly match those of `blk', so use `k'. + subtractBuf[k] = temp; + borrowIn = borrowOut; + } + // No more extra iteration to deal with `bHigh'. + // Roll-over a borrow as necessary. + for (; k < origLen && borrowIn; k++) { + borrowIn = (blk[k] == 0); + subtractBuf[k] = blk[k] - 1; + } + /* + * If the subtraction was performed successfully (!borrowIn), + * set bit i2 in block i of the quotient. + * + * Then, copy the portion of subtractBuf filled by the subtraction + * back to *this. This portion starts with block i and ends-- + * where? Not necessarily at block `i + b.len'! Well, we + * increased k every time we saved a block into subtractBuf, so + * the region of subtractBuf we copy is just [i, k). + */ + if (!borrowIn) { + q.blk[i] |= (Blk(1) << i2); + while (k > i) { + k--; + blk[k] = subtractBuf[k]; + } + } + } + } + // Zap possible leading zero in quotient + if (q.blk[q.len - 1] == 0) + q.len--; + // Zap any/all leading zeros in remainder + zapLeadingZeros(); + // Deallocate subtractBuf. + // (Thanks to Brad Spencer for noticing my accidental omission of this!) + delete [] subtractBuf; +} + +/* BITWISE OPERATORS + * These are straightforward blockwise operations except that they differ in + * the output length and the necessity of zapLeadingZeros. */ + +void BigUnsigned::bitAnd(const BigUnsigned &a, const BigUnsigned &b) { + DTRT_ALIASED(this == &a || this == &b, bitAnd(a, b)); + // The bitwise & can't be longer than either operand. + len = (a.len >= b.len) ? b.len : a.len; + allocate(len); + Index i; + for (i = 0; i < len; i++) + blk[i] = a.blk[i] & b.blk[i]; + zapLeadingZeros(); +} + +void BigUnsigned::bitOr(const BigUnsigned &a, const BigUnsigned &b) { + DTRT_ALIASED(this == &a || this == &b, bitOr(a, b)); + Index i; + const BigUnsigned *a2, *b2; + if (a.len >= b.len) { + a2 = &a; + b2 = &b; + } else { + a2 = &b; + b2 = &a; + } + allocate(a2->len); + for (i = 0; i < b2->len; i++) + blk[i] = a2->blk[i] | b2->blk[i]; + for (; i < a2->len; i++) + blk[i] = a2->blk[i]; + len = a2->len; + // Doesn't need zapLeadingZeros. +} + +void BigUnsigned::bitXor(const BigUnsigned &a, const BigUnsigned &b) { + DTRT_ALIASED(this == &a || this == &b, bitXor(a, b)); + Index i; + const BigUnsigned *a2, *b2; + if (a.len >= b.len) { + a2 = &a; + b2 = &b; + } else { + a2 = &b; + b2 = &a; + } + allocate(a2->len); + for (i = 0; i < b2->len; i++) + blk[i] = a2->blk[i] ^ b2->blk[i]; + for (; i < a2->len; i++) + blk[i] = a2->blk[i]; + len = a2->len; + zapLeadingZeros(); +} + +void BigUnsigned::bitShiftLeft(const BigUnsigned &a, int b) { + DTRT_ALIASED(this == &a, bitShiftLeft(a, b)); + if (b < 0) { + if (b << 1 == 0) + throw "BigUnsigned::bitShiftLeft: " + "Pathological shift amount not implemented"; + else { + bitShiftRight(a, -b); + return; + } + } + Index shiftBlocks = b / N; + unsigned int shiftBits = b % N; + // + 1: room for high bits nudged left into another block + len = a.len + shiftBlocks + 1; + allocate(len); + Index i, j; + for (i = 0; i < shiftBlocks; i++) + blk[i] = 0; + for (j = 0, i = shiftBlocks; j <= a.len; j++, i++) + blk[i] = getShiftedBlock(a, j, shiftBits); + // Zap possible leading zero + if (blk[len - 1] == 0) + len--; +} + +void BigUnsigned::bitShiftRight(const BigUnsigned &a, int b) { + DTRT_ALIASED(this == &a, bitShiftRight(a, b)); + if (b < 0) { + if (b << 1 == 0) + throw "BigUnsigned::bitShiftRight: " + "Pathological shift amount not implemented"; + else { + bitShiftLeft(a, -b); + return; + } + } + // This calculation is wacky, but expressing the shift as a left bit shift + // within each block lets us use getShiftedBlock. + Index rightShiftBlocks = (b + N - 1) / N; + unsigned int leftShiftBits = N * rightShiftBlocks - b; + // Now (N * rightShiftBlocks - leftShiftBits) == b + // and 0 <= leftShiftBits < N. + if (rightShiftBlocks >= a.len + 1) { + // All of a is guaranteed to be shifted off, even considering the left + // bit shift. + len = 0; + return; + } + // Now we're allocating a positive amount. + // + 1: room for high bits nudged left into another block + len = a.len + 1 - rightShiftBlocks; + allocate(len); + Index i, j; + for (j = rightShiftBlocks, i = 0; j <= a.len; j++, i++) + blk[i] = getShiftedBlock(a, j, leftShiftBits); + // Zap possible leading zero + if (blk[len - 1] == 0) + len--; +} + +// INCREMENT/DECREMENT OPERATORS + +// Prefix increment +void BigUnsigned::operator ++() { + Index i; + bool carry = true; + for (i = 0; i < len && carry; i++) { + blk[i]++; + carry = (blk[i] == 0); + } + if (carry) { + // Allocate and then increase length, as in divideWithRemainder + allocateAndCopy(len + 1); + len++; + blk[i] = 1; + } +} + +// Postfix increment: same as prefix +void BigUnsigned::operator ++(int) { + operator ++(); +} + +// Prefix decrement +void BigUnsigned::operator --() { + if (len == 0) + throw "BigUnsigned::operator --(): Cannot decrement an unsigned zero"; + Index i; + bool borrow = true; + for (i = 0; borrow; i++) { + borrow = (blk[i] == 0); + blk[i]--; + } + // Zap possible leading zero (there can only be one) + if (blk[len - 1] == 0) + len--; +} + +// Postfix decrement: same as prefix +void BigUnsigned::operator --(int) { + operator --(); +} diff --git a/bigint/BigUnsigned.hh b/bigint/BigUnsigned.hh new file mode 100644 index 000000000..9228753c8 --- /dev/null +++ b/bigint/BigUnsigned.hh @@ -0,0 +1,418 @@ +#ifndef BIGUNSIGNED_H +#define BIGUNSIGNED_H + +#include "NumberlikeArray.hh" + +/* A BigUnsigned object represents a nonnegative integer of size limited only by + * available memory. BigUnsigneds support most mathematical operators and can + * be converted to and from most primitive integer types. + * + * The number is stored as a NumberlikeArray of unsigned longs as if it were + * written in base 256^sizeof(unsigned long). The least significant block is + * first, and the length is such that the most significant block is nonzero. */ +class BigUnsigned : protected NumberlikeArray { + +public: + // Enumeration for the result of a comparison. + enum CmpRes { less = -1, equal = 0, greater = 1 }; + + // BigUnsigneds are built with a Blk type of unsigned long. + typedef unsigned long Blk; + + typedef NumberlikeArray::Index Index; + using NumberlikeArray::N; + +protected: + // Creates a BigUnsigned with a capacity; for internal use. + BigUnsigned(int, Index c) : NumberlikeArray(0, c) {} + + // Decreases len to eliminate any leading zero blocks. + void zapLeadingZeros() { + while (len > 0 && blk[len - 1] == 0) + len--; + } + +public: + // Constructs zero. + BigUnsigned() : NumberlikeArray() {} + + // Copy constructor + BigUnsigned(const BigUnsigned &x) : NumberlikeArray(x) {} + + // Assignment operator + void operator=(const BigUnsigned &x) { + NumberlikeArray::operator =(x); + } + + // Constructor that copies from a given array of blocks. + BigUnsigned(const Blk *b, Index blen) : NumberlikeArray(b, blen) { + // Eliminate any leading zeros we may have been passed. + zapLeadingZeros(); + } + + // Destructor. NumberlikeArray does the delete for us. + ~BigUnsigned() {} + + // Constructors from primitive integer types + BigUnsigned(unsigned long x); + BigUnsigned( long x); + BigUnsigned(unsigned int x); + BigUnsigned( int x); + BigUnsigned(unsigned short x); + BigUnsigned( short x); +protected: + // Helpers + template void initFromPrimitive (X x); + template void initFromSignedPrimitive(X x); +public: + + /* Converters to primitive integer types + * The implicit conversion operators caused trouble, so these are now + * named. */ + unsigned long toUnsignedLong () const; + long toLong () const; + unsigned int toUnsignedInt () const; + int toInt () const; + unsigned short toUnsignedShort() const; + short toShort () const; +protected: + // Helpers + template X convertToSignedPrimitive() const; + template X convertToPrimitive () const; +public: + + // BIT/BLOCK ACCESSORS + + // Expose these from NumberlikeArray directly. + using NumberlikeArray::getCapacity; + using NumberlikeArray::getLength; + + /* Returns the requested block, or 0 if it is beyond the length (as if + * the number had 0s infinitely to the left). */ + Blk getBlock(Index i) const { return i >= len ? 0 : blk[i]; } + /* Sets the requested block. The number grows or shrinks as necessary. */ + void setBlock(Index i, Blk newBlock); + + // The number is zero if and only if the canonical length is zero. + bool isZero() const { return NumberlikeArray::isEmpty(); } + + /* Returns the length of the number in bits, i.e., zero if the number + * is zero and otherwise one more than the largest value of bi for + * which getBit(bi) returns true. */ + Index bitLength() const; + /* Get the state of bit bi, which has value 2^bi. Bits beyond the + * number's length are considered to be 0. */ + bool getBit(Index bi) const { + return (getBlock(bi / N) & (Blk(1) << (bi % N))) != 0; + } + /* Sets the state of bit bi to newBit. The number grows or shrinks as + * necessary. */ + void setBit(Index bi, bool newBit); + + // COMPARISONS + + // Compares this to x like Perl's <=> + CmpRes compareTo(const BigUnsigned &x) const; + + // Ordinary comparison operators + bool operator ==(const BigUnsigned &x) const { + return NumberlikeArray::operator ==(x); + } + bool operator !=(const BigUnsigned &x) const { + return NumberlikeArray::operator !=(x); + } + bool operator < (const BigUnsigned &x) const { return compareTo(x) == less ; } + bool operator <=(const BigUnsigned &x) const { return compareTo(x) != greater; } + bool operator >=(const BigUnsigned &x) const { return compareTo(x) != less ; } + bool operator > (const BigUnsigned &x) const { return compareTo(x) == greater; } + + /* + * BigUnsigned and BigInteger both provide three kinds of operators. + * Here ``big-integer'' refers to BigInteger or BigUnsigned. + * + * (1) Overloaded ``return-by-value'' operators: + * +, -, *, /, %, unary -, &, |, ^, <<, >>. + * Big-integer code using these operators looks identical to code using + * the primitive integer types. These operators take one or two + * big-integer inputs and return a big-integer result, which can then + * be assigned to a BigInteger variable or used in an expression. + * Example: + * BigInteger a(1), b = 1; + * BigInteger c = a + b; + * + * (2) Overloaded assignment operators: + * +=, -=, *=, /=, %=, flipSign, &=, |=, ^=, <<=, >>=, ++, --. + * Again, these are used on big integers just like on ints. They take + * one writable big integer that both provides an operand and receives a + * result. Most also take a second read-only operand. + * Example: + * BigInteger a(1), b(1); + * a += b; + * + * (3) Copy-less operations: `add', `subtract', etc. + * These named methods take operands as arguments and store the result + * in the receiver (*this), avoiding unnecessary copies and allocations. + * `divideWithRemainder' is special: it both takes the dividend from and + * stores the remainder into the receiver, and it takes a separate + * object in which to store the quotient. NOTE: If you are wondering + * why these don't return a value, you probably mean to use the + * overloaded return-by-value operators instead. + * + * Examples: + * BigInteger a(43), b(7), c, d; + * + * c = a + b; // Now c == 50. + * c.add(a, b); // Same effect but without the two copies. + * + * c.divideWithRemainder(b, d); + * // 50 / 7; now d == 7 (quotient) and c == 1 (remainder). + * + * // ``Aliased'' calls now do the right thing using a temporary + * // copy, but see note on `divideWithRemainder'. + * a.add(a, b); + */ + + // COPY-LESS OPERATIONS + + // These 8: Arguments are read-only operands, result is saved in *this. + void add(const BigUnsigned &a, const BigUnsigned &b); + void subtract(const BigUnsigned &a, const BigUnsigned &b); + void multiply(const BigUnsigned &a, const BigUnsigned &b); + void bitAnd(const BigUnsigned &a, const BigUnsigned &b); + void bitOr(const BigUnsigned &a, const BigUnsigned &b); + void bitXor(const BigUnsigned &a, const BigUnsigned &b); + /* Negative shift amounts translate to opposite-direction shifts, + * except for -2^(8*sizeof(int)-1) which is unimplemented. */ + void bitShiftLeft(const BigUnsigned &a, int b); + void bitShiftRight(const BigUnsigned &a, int b); + + /* `a.divideWithRemainder(b, q)' is like `q = a / b, a %= b'. + * / and % use semantics similar to Knuth's, which differ from the + * primitive integer semantics under division by zero. See the + * implementation in BigUnsigned.cc for details. + * `a.divideWithRemainder(b, a)' throws an exception: it doesn't make + * sense to write quotient and remainder into the same variable. */ + void divideWithRemainder(const BigUnsigned &b, BigUnsigned &q); + + /* `divide' and `modulo' are no longer offered. Use + * `divideWithRemainder' instead. */ + + // OVERLOADED RETURN-BY-VALUE OPERATORS + BigUnsigned operator +(const BigUnsigned &x) const; + BigUnsigned operator -(const BigUnsigned &x) const; + BigUnsigned operator *(const BigUnsigned &x) const; + BigUnsigned operator /(const BigUnsigned &x) const; + BigUnsigned operator %(const BigUnsigned &x) const; + /* OK, maybe unary minus could succeed in one case, but it really + * shouldn't be used, so it isn't provided. */ + BigUnsigned operator &(const BigUnsigned &x) const; + BigUnsigned operator |(const BigUnsigned &x) const; + BigUnsigned operator ^(const BigUnsigned &x) const; + BigUnsigned operator <<(int b) const; + BigUnsigned operator >>(int b) const; + + // OVERLOADED ASSIGNMENT OPERATORS + void operator +=(const BigUnsigned &x); + void operator -=(const BigUnsigned &x); + void operator *=(const BigUnsigned &x); + void operator /=(const BigUnsigned &x); + void operator %=(const BigUnsigned &x); + void operator &=(const BigUnsigned &x); + void operator |=(const BigUnsigned &x); + void operator ^=(const BigUnsigned &x); + void operator <<=(int b); + void operator >>=(int b); + + /* INCREMENT/DECREMENT OPERATORS + * To discourage messy coding, these do not return *this, so prefix + * and postfix behave the same. */ + void operator ++( ); + void operator ++(int); + void operator --( ); + void operator --(int); + + // Helper function that needs access to BigUnsigned internals + friend Blk getShiftedBlock(const BigUnsigned &num, Index x, + unsigned int y); + + // See BigInteger.cc. + template + friend X convertBigUnsignedToPrimitiveAccess(const BigUnsigned &a); +}; + +/* Implementing the return-by-value and assignment operators in terms of the + * copy-less operations. The copy-less operations are responsible for making + * any necessary temporary copies to work around aliasing. */ + +inline BigUnsigned BigUnsigned::operator +(const BigUnsigned &x) const { + BigUnsigned ans; + ans.add(*this, x); + return ans; +} +inline BigUnsigned BigUnsigned::operator -(const BigUnsigned &x) const { + BigUnsigned ans; + ans.subtract(*this, x); + return ans; +} +inline BigUnsigned BigUnsigned::operator *(const BigUnsigned &x) const { + BigUnsigned ans; + ans.multiply(*this, x); + return ans; +} +inline BigUnsigned BigUnsigned::operator /(const BigUnsigned &x) const { + if (x.isZero()) throw "BigUnsigned::operator /: division by zero"; + BigUnsigned q, r; + r = *this; + r.divideWithRemainder(x, q); + return q; +} +inline BigUnsigned BigUnsigned::operator %(const BigUnsigned &x) const { + if (x.isZero()) throw "BigUnsigned::operator %: division by zero"; + BigUnsigned q, r; + r = *this; + r.divideWithRemainder(x, q); + return r; +} +inline BigUnsigned BigUnsigned::operator &(const BigUnsigned &x) const { + BigUnsigned ans; + ans.bitAnd(*this, x); + return ans; +} +inline BigUnsigned BigUnsigned::operator |(const BigUnsigned &x) const { + BigUnsigned ans; + ans.bitOr(*this, x); + return ans; +} +inline BigUnsigned BigUnsigned::operator ^(const BigUnsigned &x) const { + BigUnsigned ans; + ans.bitXor(*this, x); + return ans; +} +inline BigUnsigned BigUnsigned::operator <<(int b) const { + BigUnsigned ans; + ans.bitShiftLeft(*this, b); + return ans; +} +inline BigUnsigned BigUnsigned::operator >>(int b) const { + BigUnsigned ans; + ans.bitShiftRight(*this, b); + return ans; +} + +inline void BigUnsigned::operator +=(const BigUnsigned &x) { + add(*this, x); +} +inline void BigUnsigned::operator -=(const BigUnsigned &x) { + subtract(*this, x); +} +inline void BigUnsigned::operator *=(const BigUnsigned &x) { + multiply(*this, x); +} +inline void BigUnsigned::operator /=(const BigUnsigned &x) { + if (x.isZero()) throw "BigUnsigned::operator /=: division by zero"; + /* The following technique is slightly faster than copying *this first + * when x is large. */ + BigUnsigned q; + divideWithRemainder(x, q); + // *this contains the remainder, but we overwrite it with the quotient. + *this = q; +} +inline void BigUnsigned::operator %=(const BigUnsigned &x) { + if (x.isZero()) throw "BigUnsigned::operator %=: division by zero"; + BigUnsigned q; + // Mods *this by x. Don't care about quotient left in q. + divideWithRemainder(x, q); +} +inline void BigUnsigned::operator &=(const BigUnsigned &x) { + bitAnd(*this, x); +} +inline void BigUnsigned::operator |=(const BigUnsigned &x) { + bitOr(*this, x); +} +inline void BigUnsigned::operator ^=(const BigUnsigned &x) { + bitXor(*this, x); +} +inline void BigUnsigned::operator <<=(int b) { + bitShiftLeft(*this, b); +} +inline void BigUnsigned::operator >>=(int b) { + bitShiftRight(*this, b); +} + +/* Templates for conversions of BigUnsigned to and from primitive integers. + * BigInteger.cc needs to instantiate convertToPrimitive, and the uses in + * BigUnsigned.cc didn't do the trick; I think g++ inlined convertToPrimitive + * instead of generating linkable instantiations. So for consistency, I put + * all the templates here. */ + +// CONSTRUCTION FROM PRIMITIVE INTEGERS + +/* Initialize this BigUnsigned from the given primitive integer. The same + * pattern works for all primitive integer types, so I put it into a template to + * reduce code duplication. (Don't worry: this is protected and we instantiate + * it only with primitive integer types.) Type X could be signed, but x is + * known to be nonnegative. */ +template +void BigUnsigned::initFromPrimitive(X x) { + if (x == 0) + ; // NumberlikeArray already initialized us to zero. + else { + // Create a single block. blk is NULL; no need to delete it. + cap = 1; + blk = new Blk[1]; + len = 1; + blk[0] = Blk(x); + } +} + +/* Ditto, but first check that x is nonnegative. I could have put the check in + * initFromPrimitive and let the compiler optimize it out for unsigned-type + * instantiations, but I wanted to avoid the warning stupidly issued by g++ for + * a condition that is constant in *any* instantiation, even if not in all. */ +template +void BigUnsigned::initFromSignedPrimitive(X x) { + if (x < 0) + throw "BigUnsigned constructor: " + "Cannot construct a BigUnsigned from a negative number"; + else + initFromPrimitive(x); +} + +// CONVERSION TO PRIMITIVE INTEGERS + +/* Template with the same idea as initFromPrimitive. This might be slightly + * slower than the previous version with the masks, but it's much shorter and + * clearer, which is the library's stated goal. */ +template +X BigUnsigned::convertToPrimitive() const { + if (len == 0) + // The number is zero; return zero. + return 0; + else if (len == 1) { + // The single block might fit in an X. Try the conversion. + X x = X(blk[0]); + // Make sure the result accurately represents the block. + if (Blk(x) == blk[0]) + // Successful conversion. + return x; + // Otherwise fall through. + } + throw "BigUnsigned::to: " + "Value is too big to fit in the requested type"; +} + +/* Wrap the above in an x >= 0 test to make sure we got a nonnegative result, + * not a negative one that happened to convert back into the correct nonnegative + * one. (E.g., catch incorrect conversion of 2^31 to the long -2^31.) Again, + * separated to avoid a g++ warning. */ +template +X BigUnsigned::convertToSignedPrimitive() const { + X x = convertToPrimitive(); + if (x >= 0) + return x; + else + throw "BigUnsigned::to(Primitive): " + "Value is too big to fit in the requested type"; +} + +#endif diff --git a/bigint/BigUnsignedInABase.cc b/bigint/BigUnsignedInABase.cc new file mode 100644 index 000000000..999faaf2d --- /dev/null +++ b/bigint/BigUnsignedInABase.cc @@ -0,0 +1,125 @@ +#include "BigUnsignedInABase.hh" + +BigUnsignedInABase::BigUnsignedInABase(const Digit *d, Index l, Base base) + : NumberlikeArray(d, l), base(base) { + // Check the base + if (base < 2) + throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): The base must be at least 2"; + + // Validate the digits. + for (Index i = 0; i < l; i++) + if (blk[i] >= base) + throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): A digit is too large for the specified base"; + + // Eliminate any leading zeros we may have been passed. + zapLeadingZeros(); +} + +namespace { + unsigned int bitLen(unsigned int x) { + unsigned int len = 0; + while (x > 0) { + x >>= 1; + len++; + } + return len; + } + unsigned int ceilingDiv(unsigned int a, unsigned int b) { + return (a + b - 1) / b; + } +} + +BigUnsignedInABase::BigUnsignedInABase(const BigUnsigned &x, Base base) { + // Check the base + if (base < 2) + throw "BigUnsignedInABase(BigUnsigned, Base): The base must be at least 2"; + this->base = base; + + // Get an upper bound on how much space we need + int maxBitLenOfX = x.getLength() * BigUnsigned::N; + int minBitsPerDigit = bitLen(base) - 1; + int maxDigitLenOfX = ceilingDiv(maxBitLenOfX, minBitsPerDigit); + len = maxDigitLenOfX; // Another change to comply with `staying in bounds'. + allocate(len); // Get the space + + BigUnsigned x2(x), buBase(base); + Index digitNum = 0; + + while (!x2.isZero()) { + // Get last digit. This is like `lastDigit = x2 % buBase, x2 /= buBase'. + BigUnsigned lastDigit(x2); + lastDigit.divideWithRemainder(buBase, x2); + // Save the digit. + blk[digitNum] = lastDigit.toUnsignedShort(); + // Move on. We can't run out of room: we figured it out above. + digitNum++; + } + + // Save the actual length. + len = digitNum; +} + +BigUnsignedInABase::operator BigUnsigned() const { + BigUnsigned ans(0), buBase(base), temp; + Index digitNum = len; + while (digitNum > 0) { + digitNum--; + temp.multiply(ans, buBase); + ans.add(temp, BigUnsigned(blk[digitNum])); + } + return ans; +} + +BigUnsignedInABase::BigUnsignedInABase(const std::string &s, Base base) { + // Check the base. + if (base > 36) + throw "BigUnsignedInABase(std::string, Base): The default string conversion routines use the symbol set 0-9, A-Z and therefore support only up to base 36. You tried a conversion with a base over 36; write your own string conversion routine."; + // Save the base. + // This pattern is seldom seen in C++, but the analogous ``this.'' is common in Java. + this->base = base; + + // `s.length()' is a `size_t', while `len' is a `NumberlikeArray::Index', + // also known as an `unsigned int'. Some compilers warn without this cast. + len = Index(s.length()); + allocate(len); + + Index digitNum, symbolNumInString; + for (digitNum = 0; digitNum < len; digitNum++) { + symbolNumInString = len - 1 - digitNum; + char theSymbol = s[symbolNumInString]; + if (theSymbol >= '0' && theSymbol <= '9') + blk[digitNum] = theSymbol - '0'; + else if (theSymbol >= 'A' && theSymbol <= 'Z') + blk[digitNum] = theSymbol - 'A' + 10; + else if (theSymbol >= 'a' && theSymbol <= 'z') + blk[digitNum] = theSymbol - 'a' + 10; + else + throw "BigUnsignedInABase(std::string, Base): Bad symbol in input. Only 0-9, A-Z, a-z are accepted."; + + if (blk[digitNum] >= base) + throw "BigUnsignedInABase::BigUnsignedInABase(const Digit *, Index, Base): A digit is too large for the specified base"; + } + zapLeadingZeros(); +} + +BigUnsignedInABase::operator std::string() const { + if (base > 36) + throw "BigUnsignedInABase ==> std::string: The default string conversion routines use the symbol set 0-9, A-Z and therefore support only up to base 36. You tried a conversion with a base over 36; write your own string conversion routine."; + if (len == 0) + return std::string("0"); + // Some compilers don't have push_back, so use a char * buffer instead. + char *s = new char[len + 1]; + s[len] = '\0'; + Index digitNum, symbolNumInString; + for (symbolNumInString = 0; symbolNumInString < len; symbolNumInString++) { + digitNum = len - 1 - symbolNumInString; + Digit theDigit = blk[digitNum]; + if (theDigit < 10) + s[symbolNumInString] = char('0' + theDigit); + else + s[symbolNumInString] = char('A' + theDigit - 10); + } + std::string s2(s); + delete [] s; + return s2; +} diff --git a/bigint/BigUnsignedInABase.hh b/bigint/BigUnsignedInABase.hh new file mode 100644 index 000000000..0ea89c6ef --- /dev/null +++ b/bigint/BigUnsignedInABase.hh @@ -0,0 +1,122 @@ +#ifndef BIGUNSIGNEDINABASE_H +#define BIGUNSIGNEDINABASE_H + +#include "NumberlikeArray.hh" +#include "BigUnsigned.hh" +#include + +/* + * A BigUnsignedInABase object represents a nonnegative integer of size limited + * only by available memory, represented in a user-specified base that can fit + * in an `unsigned short' (most can, and this saves memory). + * + * BigUnsignedInABase is intended as an intermediary class with little + * functionality of its own. BigUnsignedInABase objects can be constructed + * from, and converted to, BigUnsigneds (requiring multiplication, mods, etc.) + * and `std::string's (by switching digit values for appropriate characters). + * + * BigUnsignedInABase is similar to BigUnsigned. Note the following: + * + * (1) They represent the number in exactly the same way, except that + * BigUnsignedInABase uses ``digits'' (or Digit) where BigUnsigned uses + * ``blocks'' (or Blk). + * + * (2) Both use the management features of NumberlikeArray. (In fact, my desire + * to add a BigUnsignedInABase class without duplicating a lot of code led me to + * introduce NumberlikeArray.) + * + * (3) The only arithmetic operation supported by BigUnsignedInABase is an + * equality test. Use BigUnsigned for arithmetic. + */ + +class BigUnsignedInABase : protected NumberlikeArray { + +public: + // The digits of a BigUnsignedInABase are unsigned shorts. + typedef unsigned short Digit; + // That's also the type of a base. + typedef Digit Base; + +protected: + // The base in which this BigUnsignedInABase is expressed + Base base; + + // Creates a BigUnsignedInABase with a capacity; for internal use. + BigUnsignedInABase(int, Index c) : NumberlikeArray(0, c) {} + + // Decreases len to eliminate any leading zero digits. + void zapLeadingZeros() { + while (len > 0 && blk[len - 1] == 0) + len--; + } + +public: + // Constructs zero in base 2. + BigUnsignedInABase() : NumberlikeArray(), base(2) {} + + // Copy constructor + BigUnsignedInABase(const BigUnsignedInABase &x) : NumberlikeArray(x), base(x.base) {} + + // Assignment operator + void operator =(const BigUnsignedInABase &x) { + NumberlikeArray::operator =(x); + base = x.base; + } + + // Constructor that copies from a given array of digits. + BigUnsignedInABase(const Digit *d, Index l, Base base); + + // Destructor. NumberlikeArray does the delete for us. + ~BigUnsignedInABase() {} + + // LINKS TO BIGUNSIGNED + BigUnsignedInABase(const BigUnsigned &x, Base base); + operator BigUnsigned() const; + + /* LINKS TO STRINGS + * + * These use the symbols ``0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'' to + * represent digits of 0 through 35. When parsing strings, lowercase is + * also accepted. + * + * All string representations are big-endian (big-place-value digits + * first). (Computer scientists have adopted zero-based counting; why + * can't they tolerate little-endian numbers?) + * + * No string representation has a ``base indicator'' like ``0x''. + * + * An exception is made for zero: it is converted to ``0'' and not the + * empty string. + * + * If you want different conventions, write your own routines to go + * between BigUnsignedInABase and strings. It's not hard. + */ + operator std::string() const; + BigUnsignedInABase(const std::string &s, Base base); + +public: + + // ACCESSORS + Base getBase() const { return base; } + + // Expose these from NumberlikeArray directly. + using NumberlikeArray::getCapacity; + using NumberlikeArray::getLength; + + /* Returns the requested digit, or 0 if it is beyond the length (as if + * the number had 0s infinitely to the left). */ + Digit getDigit(Index i) const { return i >= len ? 0 : blk[i]; } + + // The number is zero if and only if the canonical length is zero. + bool isZero() const { return NumberlikeArray::isEmpty(); } + + /* Equality test. For the purposes of this test, two BigUnsignedInABase + * values must have the same base to be equal. */ + bool operator ==(const BigUnsignedInABase &x) const { + return base == x.base && NumberlikeArray::operator ==(x); + } + bool operator !=(const BigUnsignedInABase &x) const { return !operator ==(x); } + +}; + +#endif diff --git a/bigint/ChangeLog b/bigint/ChangeLog new file mode 100644 index 000000000..ac6927c40 --- /dev/null +++ b/bigint/ChangeLog @@ -0,0 +1,146 @@ + Change Log + +These entries tell you what was added, fixed, or improved in each version as +compared to the previous one. In case you haven't noticed, a version number +roughly corresponds to the release date of that version in `YYYY.MM.DD[.N]' +format, where `.N' goes `.2', `.3', etc. if there are multiple versions on the +same day. The topmost version listed is the one you have. + +2010.04.30 +---------- +- Strengthen the advice about build/IDE configuration in the README. + +2009.05.03 +---------- +- BigUnsigned::{get,set}Bit: Change two remaining `1 <<' to `Blk(1) <<' to work + on systems where sizeof(unsigned int) != sizeof(Blk). Bug reported by Brad + Spencer. +- dataToBigInteger: Change a `delete' to `delete []' to avoid leaking memory. + Bug reported by Nicolás Carrasco. + +2009.03.26 +---------- +- BigUnsignedInABase(std::string) Reject digits too big for the base. + Bug reported by Niakam Kazemi. + +2008.07.20 +---------- +Dennis Yew pointed out serious problems with ambiguities and unwanted +conversions when mixing BigInteger/BigUnsigned and primitive integers. To fix +these, I removed the implicit conversions from BigInteger/BigUnsigned to +primitive integers and from BigInteger to BigUnsigned. Removing the +BigInteger-to-BigUnsigned conversion required changing BigInteger to have a +BigUnsigned field instead of inheriting from it; this was a complex task but +ultimately gave a saner design. At the same time, I went through the entire +codebase, making the formatting and comments prettier and reworking anything I +thought was unclear. I also added a testsuite (currently for 32-bit systems +only); it doesn't yet cover the entire library but should help to ensure that +things work the way they should. + +A number of changes from version 2007.07.07 break compatibility with existing +code that uses the library, but updating that code should be pretty easy: +- BigInteger can no longer be implicitly converted to BigUnsigned. Use + getMagnitude() instead. +- BigUnsigned and BigInteger can no longer be implicitly converted to primitive + integers. Use the toInt() family of functions instead. +- The easy* functions have been renamed to more mature names: + bigUnsignedToString, bigIntegerToString, stringToBigUnsigned, + stringToBigInteger, dataToBigInteger. +- BigInteger no longer supports bitwise operations. Get the magnitude with + getMagnitude() and operate on that instead. +- The old {BigUnsigned,BigInteger}::{divide,modulo} copy-less options have been + removed. Use divideWithRemainder instead. +- Added a base argument to BigUnsignedInABase's digit-array constructor. I + ope no one used that constructor in its broken state anyway. + +Other notable changes: +- Added BigUnsigned functions setBlock, bitLength, getBit, setBit. +- The bit-shifting operations now support negative shift amounts, which shift in + the other direction. +- Added some big-integer algorithms in BigIntegerAlgorithms.hh: gcd, + extendedEuclidean, modinv, modexp. + +2007.07.07 +---------- +Update the "Running the sample program produces this output:" comment in +sample.cc for the bitwise operators. + +2007.06.14 +---------- +- Implement << and >> for BigUnsigned in response to email from Marco Schulze. +- Fix name: DOTR_ALIASED -> DTRT_ALIASED. +- Demonstrate all bitwise operators (&, |, ^, <<, >>) in sample.cc. + +2007.02.16 +---------- +Boris Dessy pointed out that the library threw an exception on "a *= a", so I changed all the put-here operations to handle aliased calls correctly using a temporary copy instead of throwing exceptions. + +2006.08.14 +---------- +In BigUnsigned::bitXor, change allocate(b2->len) to allocate(a2->len): we should allocate enough space for the longer number, not the shorter one! Thanks to Sriram Sankararaman for pointing this out. + +2006.05.03 +---------- +I ran the sample program using valgrind and discovered a `delete s' that should be `delete [] s' and a `len++' before an `allocateAndCopy(len)' that should have been after an `allocateAndCopy(len + 1)'. I fixed both. Yay for valgrind! + +2006.05.01 +---------- +I fixed incorrect results reported by Mohand Mezmaz and related memory corruption on platforms where Blk is bigger than int. I replaced (1 << x) with (Blk(1) << x) in two places in BigUnsigned.cc. + +2006.04.24 +---------- +Two bug fixes: BigUnsigned "++x" no longer segfaults when x grows in length, and BigUnsigned == and != are now redeclared so as to be usable. I redid the Makefile: I removed the *.tag mechanism and hard-coded the library's header dependencies, I added comments, and I made the Makefile more useful for building one's own programs instead of just the sample. + +2006.02.26 +---------- +A few tweaks in preparation for a group to distribute the library. The project Web site has moved; I updated the references. I fixed a typo and added a missing function in NumberlikeArray.hh. I'm using Eclipse now, so you get Eclipse project files. + +2005.03.30 +---------- +Sam Larkin found a bug in `BigInteger::subtract'; I fixed it. + +2005.01.18 +---------- +I fixed some problems with `easyDataToBI'. Due to some multiply declared variables, this function would not compile. However, it is a template function, so the compiler parses it and doesn't compile the parsed representation until something uses the function; this is how I missed the problems. I also removed debugging output from this function. + +2005.01.17 +---------- +A fix to some out-of-bounds accesses reported by Milan Tomic (see the comment under `BigUnsigned::divideWithRemainder'). `BigUnsigned::multiply' and `BigUnsigned::divideWithRemainder' implementations neatened up a bit with the help of a function `getShiftedBlock'. I (finally!) introduced a constant `BigUnsigned::N', the number of bits in a `BigUnsigned::Blk', which varies depending on machine word size. In both code and comments, it replaces the much clunkier `8*sizeof(Blk)'. Numerous other small changes. There's a new conversion routine `easyDataToBI' that will convert almost any format of binary data to a `BigInteger'. + +I have inserted a significant number of new comments. Most explain unobvious aspects of the code. + +2005.01.06 +---------- +Some changes to the way zero-length arrays are handled by `NumberlikeArray', which fixed a memory leak reported by Milan Tomic. + +2004.12.24.2 +------------ +I tied down a couple of loose ends involving division/modulo. I added an explanation of put-here vs. overloaded operators in the sample program; this has confused too many people. Miscellaneous other improvements. + +I believe that, at this point, the Big Integer Library makes no assumptions about the word size of the machine it is using. `BigUnsigned::Blk' is always an `unsigned long', whatever that may be, and its size is computed with `sizeof' when necessary. However, just in case, I would be interested to have someone test the library on a non-32-bit machine to see if it works. + +2004.12.24 +---------- +This is a _major_ upgrade to the library. Among the things that have changed: + +I wrote the original version of the library, particularly the four ``classical algorithms'' in `BigUnsigned.cc', using array indexing. Then I rewrote it to use pointers because I thought that would be faster. But recently, I revisited the code in `BigUnsigned.cc' and found that I could not begin to understand what it was doing. + +I have decided that the drawbacks of pointers, increased coding difficulty and reduced code readability, far outweigh their speed benefits. Plus, any modern optimizing compiler should produce fast code either way. Therefore, I rewrote the library to use array indexing again. (Thank goodness for regular-expression find-and-replace. It saved me a lot of time.) + +The put-here operations `divide' and `modulo' of each of `BigUnsigned' and `BigInteger' have been supplanted by a single operation `divideWithRemainder'. Read the profuse comments for more information on its exact behavior. + +There is a new class `BigUnsignedInABase' that is like `BigUnsigned' but uses a user-specified, small base instead of `256 ^ sizeof(unsigned long)'. Much of the code common to the two has been factored out into `NumberlikeArray'. + +`BigUnsignedInABase' facilitates conversion between `BigUnsigned's and digit-by-digit string representations using `std::string'. Convenience routines to do this conversion are in `BigIntegerUtils.hh'. `iostream' compatibility has been improved. + +I would like to thank Chris Morbitzer for the e-mail message that catalyzed this major upgrade. He wanted a way to convert a string to a BigInteger. One thing just led to another, roughly in reverse order from how they are listed here. + +2004.1216 +--------- +Brad Spencer pointed out a memory leak in `BigUnsigned::divide'. It is fixed in the December 16, 2004 version. + +2004.1205 +--------- +After months of inactivity, I fixed a bug in the `BigInteger' division routine; thanks to David Allen for reporting the bug. I also added simple routines for decimal output to `std::ostream's, and there is a demo that prints out powers of 3. + +~~~~ diff --git a/bigint/Makefile b/bigint/Makefile new file mode 100644 index 000000000..3018e98e6 --- /dev/null +++ b/bigint/Makefile @@ -0,0 +1,73 @@ +# Mention default target. +all: + +# Implicit rule to compile C++ files. Modify to your taste. +%.o: %.cc + g++ -c -O2 -Wall -Wextra -pedantic $< + +# Components of the library. +library-objects = \ + BigUnsigned.o \ + BigInteger.o \ + BigIntegerAlgorithms.o \ + BigUnsignedInABase.o \ + BigIntegerUtils.o \ + +library-headers = \ + NumberlikeArray.hh \ + BigUnsigned.hh \ + BigInteger.hh \ + BigIntegerAlgorithms.hh \ + BigUnsignedInABase.hh \ + BigIntegerLibrary.hh \ + +# To ``make the library'', make all its objects using the implicit rule. +library: $(library-objects) + +# Conservatively assume that all the objects depend on all the headers. +$(library-objects): $(library-headers) + +# TESTSUITE (NOTE: Currently expects a 32-bit system) +# Compiling the testsuite. +testsuite.o: $(library-headers) +testsuite: testsuite.o $(library-objects) + g++ $^ -o $@ +# Extract the expected output from the testsuite source. +testsuite.expected: testsuite.cc + nl -ba -p -s: $< | sed -nre 's,^ +([0-9]+):.*//([^ ]),Line \1: \2,p' >$@ +# Run the testsuite. +.PHONY: test +test: testsuite testsuite.expected + ./run-testsuite +testsuite-cleanfiles = \ + testsuite.o testsuite testsuite.expected \ + testsuite.out testsuite.err + +# The rules below build a program that uses the library. They are preset to +# build ``sample'' from ``sample.cc''. You can change the name(s) of the +# source file(s) and program file to build your own program, or you can write +# your own Makefile. + +# Components of the program. +program = sample +program-objects = sample.o + +# Conservatively assume all the program source files depend on all the library +# headers. You can change this if it is not the case. +$(program-objects) : $(library-headers) + +# How to link the program. The implicit rule covers individual objects. +$(program) : $(program-objects) $(library-objects) + g++ $^ -o $@ + +# Delete all generated files we know about. +clean : + rm -f $(library-objects) $(testsuite-cleanfiles) $(program-objects) $(program) + +# I removed the *.tag dependency tracking system because it had few advantages +# over manually entering all the dependencies. If there were a portable, +# reliable dependency tracking system, I'd use it, but I know of no such; +# cons and depcomp are almost good enough. + +# Come back and define default target. +all : library $(program) diff --git a/bigint/NumberlikeArray.hh b/bigint/NumberlikeArray.hh new file mode 100644 index 000000000..53c8e5be8 --- /dev/null +++ b/bigint/NumberlikeArray.hh @@ -0,0 +1,177 @@ +#ifndef NUMBERLIKEARRAY_H +#define NUMBERLIKEARRAY_H + +// Make sure we have NULL. +#ifndef NULL +#define NULL 0 +#endif + +/* A NumberlikeArray object holds a heap-allocated array of Blk with a + * length and a capacity and provides basic memory management features. + * BigUnsigned and BigUnsignedInABase both subclass it. + * + * NumberlikeArray provides no information hiding. Subclasses should use + * nonpublic inheritance and manually expose members as desired using + * declarations like this: + * + * public: + * NumberlikeArray< the-type-argument >::getLength; + */ +template +class NumberlikeArray { +public: + + // Type for the index of a block in the array + typedef unsigned int Index; + // The number of bits in a block, defined below. + static const unsigned int N; + + // The current allocated capacity of this NumberlikeArray (in blocks) + Index cap; + // The actual length of the value stored in this NumberlikeArray (in blocks) + Index len; + // Heap-allocated array of the blocks (can be NULL if len == 0) + Blk *blk; + + // Constructs a ``zero'' NumberlikeArray with the given capacity. + NumberlikeArray(Index c) : cap(c), len(0) { + blk = (cap > 0) ? (new Blk[cap]) : NULL; + } + + /* Constructs a zero NumberlikeArray without allocating a backing array. + * A subclass that doesn't know the needed capacity at initialization + * time can use this constructor and then overwrite blk without first + * deleting it. */ + NumberlikeArray() : cap(0), len(0) { + blk = NULL; + } + + // Destructor. Note that `delete NULL' is a no-op. + ~NumberlikeArray() { + delete [] blk; + } + + /* Ensures that the array has at least the requested capacity; may + * destroy the contents. */ + void allocate(Index c); + + /* Ensures that the array has at least the requested capacity; does not + * destroy the contents. */ + void allocateAndCopy(Index c); + + // Copy constructor + NumberlikeArray(const NumberlikeArray &x); + + // Assignment operator + void operator=(const NumberlikeArray &x); + + // Constructor that copies from a given array of blocks + NumberlikeArray(const Blk *b, Index blen); + + // ACCESSORS + Index getCapacity() const { return cap; } + Index getLength() const { return len; } + Blk getBlock(Index i) const { return blk[i]; } + bool isEmpty() const { return len == 0; } + + /* Equality comparison: checks if both objects have the same length and + * equal (==) array elements to that length. Subclasses may wish to + * override. */ + bool operator ==(const NumberlikeArray &x) const; + + bool operator !=(const NumberlikeArray &x) const { + return !operator ==(x); + } +}; + +/* BEGIN TEMPLATE DEFINITIONS. They are present here so that source files that + * include this header file can generate the necessary real definitions. */ + +template +const unsigned int NumberlikeArray::N = 8 * sizeof(Blk); + +template +void NumberlikeArray::allocate(Index c) { + // If the requested capacity is more than the current capacity... + if (c > cap) { + // Delete the old number array + delete [] blk; + // Allocate the new array + cap = c; + blk = new Blk[cap]; + } +} + +template +void NumberlikeArray::allocateAndCopy(Index c) { + // If the requested capacity is more than the current capacity... + if (c > cap) { + Blk *oldBlk = blk; + // Allocate the new number array + cap = c; + blk = new Blk[cap]; + // Copy number blocks + Index i; + for (i = 0; i < len; i++) + blk[i] = oldBlk[i]; + // Delete the old array + delete [] oldBlk; + } +} + +template +NumberlikeArray::NumberlikeArray(const NumberlikeArray &x) + : len(x.len) { + // Create array + cap = len; + blk = new Blk[cap]; + // Copy blocks + Index i; + for (i = 0; i < len; i++) + blk[i] = x.blk[i]; +} + +template +void NumberlikeArray::operator=(const NumberlikeArray &x) { + /* Calls like a = a have no effect; catch them before the aliasing + * causes a problem */ + if (this == &x) + return; + // Copy length + len = x.len; + // Expand array if necessary + allocate(len); + // Copy number blocks + Index i; + for (i = 0; i < len; i++) + blk[i] = x.blk[i]; +} + +template +NumberlikeArray::NumberlikeArray(const Blk *b, Index blen) + : cap(blen), len(blen) { + // Create array + blk = new Blk[cap]; + // Copy blocks + Index i; + for (i = 0; i < len; i++) + blk[i] = b[i]; +} + +template +bool NumberlikeArray::operator ==(const NumberlikeArray &x) const { + if (len != x.len) + // Definitely unequal. + return false; + else { + // Compare corresponding blocks one by one. + Index i; + for (i = 0; i < len; i++) + if (blk[i] != x.blk[i]) + return false; + // No blocks differed, so the objects are equal. + return true; + } +} + +#endif diff --git a/bigint/README b/bigint/README new file mode 100644 index 000000000..e1842381e --- /dev/null +++ b/bigint/README @@ -0,0 +1,81 @@ + +Note by Clifford Wolf: +This version of bigint was downloaded at 2012-08-29 from +https://mattmccutchen.net/bigint/bigint-2010.04.30.tar.bz2 + +Some minor changes were made to the source code (e.g. "using" +was added to access declarations to prohibit compiler warnings). + + +============================================================================== + + C++ Big Integer Library + (see ChangeLog for version) + + http://mattmccutchen.net/bigint/ + + Written and maintained by Matt McCutchen + +You can use this library in a C++ program to do arithmetic on integers of size +limited only by your computer's memory. The library provides BigUnsigned and +BigInteger classes that represent nonnegative integers and signed integers, +respectively. Most of the C++ arithmetic operators are overloaded for these +classes, so big-integer calculations are as easy as: + + #include "BigIntegerLibrary.hh" + + BigInteger a = 65536; + cout << (a * a * a * a * a * a * a * a); + + (prints 340282366920938463463374607431768211456) + +The code in `sample.cc' demonstrates the most important features of the library. +To get started quickly, read the code and explanations in that file and run it. +If you want more detail or a feature not shown in `sample.cc', consult the +consult the actual header and source files, which are thoroughly commented. + +This library emphasizes ease of use and clarity of implementation over speed; +some users will prefer GMP (http://swox.com/gmp/), which is faster. The code is +intended to be reasonably portable across computers and modern C++ compilers; in +particular, it uses whatever word size the computer provides (32-bit, 64-bit, or +otherwise). + +Compiling programs that use the library +--------------------------------------- +The library consists of a folder full of C++ header files (`.hh') and source +files (`.cc'). Your own programs should `#include' the necessary header files +and link with the source files. A makefile that builds the sample program +(`sample.cc') is included; you can adapt it to replace the sample with your own +program. + +Alternatively, you can use your own build system or IDE. In that case, you must +put the library header files where the compiler will find them and arrange to +have your program linked with the library source files; otherwise, you will get +errors about missing header files or "undefined references". To learn how to do +this, consult the documentation for the build system or IDE; don't bother asking +me. Adding all the library files to your project will work in many IDEs but may +not be the most desirable approach. + +Resources +--------- +The library's Web site (above) provides links to released versions, the current +development version, and a mailing list for release announcements, questions, +bug reports, and other discussion of the library. I would be delighted to hear +from you if you like this library and/or find a good use for it. + +Bugs and enhancements +--------------------- +The library has been tested by me and others but is by no means bug-free. If +you find a bug, please report it, whether it comes in the form of compiling +trouble, a mathematically inaccurate result, or a memory-management blooper +(since I use Java, these are altogether too common in my C++). I generally fix +all reported bugs. You are also welcome to request enhancements, but I am +unlikely to do substantial amounts of work on enhancements at this point. + +Legal +----- +I, Matt McCutchen, the sole author of the original Big Integer Library, waive my +copyright to it, placing it in the public domain. The library comes with +absolutely no warranty. + +~~~~ diff --git a/bigint/run-testsuite b/bigint/run-testsuite new file mode 100755 index 000000000..ff7372916 --- /dev/null +++ b/bigint/run-testsuite @@ -0,0 +1,37 @@ +#!/bin/bash + +bad= + +# If you encounter the following problem with Valgrind like I did: +# https://bugzilla.redhat.com/show_bug.cgi?id=455644 +# you can pass the environment variable NO_VALGRIND=1 to run the testsuite +# without it. +if [ "$NO_VALGRIND" ]; then + cmd=(./testsuite) +else + cmd=(valgrind --error-exitcode=1 --leak-check=full ./testsuite) +fi + +set -o pipefail +# Stdout goes directly to testsuite.out; stderr goes down the pipe. +if ! "${cmd[@]}" 2>&1 >testsuite.out | tee testsuite.err; then + echo >&2 'Memory errors!' + bad=1 +fi + +if grep 'LEAK SUMMARY' testsuite.err >/dev/null; then + echo >&2 'Memory leaks!' + bad=1 +fi + +if ! diff -u testsuite.expected testsuite.out; then + echo >&2 'Output is incorrect!' + bad=1 +fi + +if [ $bad ]; then + echo >&2 'Test suite failed!' + exit 1 +else + echo 'Test suite passed.' +fi diff --git a/bigint/sample.cc b/bigint/sample.cc new file mode 100644 index 000000000..62b41df32 --- /dev/null +++ b/bigint/sample.cc @@ -0,0 +1,125 @@ +// Sample program demonstrating the use of the Big Integer Library. + +// Standard libraries +#include +#include + +// `BigIntegerLibrary.hh' includes all of the library headers. +#include "BigIntegerLibrary.hh" + +int main() { + /* The library throws `const char *' error messages when things go + * wrong. It's a good idea to catch them using a `try' block like this + * one. Your C++ compiler might need a command-line option to compile + * code that uses exceptions. */ + try { + BigInteger a; // a is 0 + int b = 535; + + /* Any primitive integer can be converted implicitly to a + * BigInteger. */ + a = b; + + /* The reverse conversion requires a method call (implicit + * conversions were previously supported but caused trouble). + * If a were too big for an int, the library would throw an + * exception. */ + b = a.toInt(); + + BigInteger c(a); // Copy a BigInteger. + + // The int literal is converted to a BigInteger. + BigInteger d(-314159265); + + /* This won't compile (at least on 32-bit machines) because the + * number is too big to be a primitive integer literal, and + * there's no such thing as a BigInteger literal. */ + //BigInteger e(3141592653589793238462643383279); + + // Instead you can convert the number from a string. + std::string s("3141592653589793238462643383279"); + BigInteger f = stringToBigInteger(s); + + // You can convert the other way too. + std::string s2 = bigIntegerToString(f); + + // f is implicitly stringified and sent to std::cout. + std::cout << f << std::endl; + + /* Let's do some math! The library overloads most of the + * mathematical operators (including assignment operators) to + * work on BigIntegers. There are also ``copy-less'' + * operations; see `BigUnsigned.hh' for details. */ + + // Arithmetic operators + BigInteger g(314159), h(265); + std::cout << (g + h) << '\n' + << (g - h) << '\n' + << (g * h) << '\n' + << (g / h) << '\n' + << (g % h) << std::endl; + + // Bitwise operators + BigUnsigned i(0xFF0000FF), j(0x0000FFFF); + // The library's << operator recognizes base flags. + std::cout.flags(std::ios::hex | std::ios::showbase); + std::cout << (i & j) << '\n' + << (i | j) << '\n' + << (i ^ j) << '\n' + // Shift distances are ordinary unsigned ints. + << (j << 21) << '\n' + << (j >> 10) << '\n'; + std::cout.flags(std::ios::dec); + + // Let's do some heavy lifting and calculate powers of 314. + int maxPower = 10; + BigUnsigned x(1), big314(314); + for (int power = 0; power <= maxPower; power++) { + std::cout << "314^" << power << " = " << x << std::endl; + x *= big314; // A BigInteger assignment operator + } + + // Some big-integer algorithms (albeit on small integers). + std::cout << gcd(BigUnsigned(60), 72) << '\n' + << modinv(BigUnsigned(7), 11) << '\n' + << modexp(BigUnsigned(314), 159, 2653) << std::endl; + + // Add your own code here to experiment with the library. + } catch(char const* err) { + std::cout << "The library threw an exception:\n" + << err << std::endl; + } + + return 0; +} + +/* +The original sample program produces this output: + +3141592653589793238462643383279 +314424 +313894 +83252135 +1185 +134 +0xFF +0xFF00FFFF +0xFF00FF00 +0x1FFFE00000 +0x3F +314^0 = 1 +314^1 = 314 +314^2 = 98596 +314^3 = 30959144 +314^4 = 9721171216 +314^5 = 3052447761824 +314^6 = 958468597212736 +314^7 = 300959139524799104 +314^8 = 94501169810786918656 +314^9 = 29673367320587092457984 +314^10 = 9317437338664347031806976 +12 +8 +1931 + +*/ diff --git a/bigint/testsuite.cc b/bigint/testsuite.cc new file mode 100644 index 000000000..7cb9768e6 --- /dev/null +++ b/bigint/testsuite.cc @@ -0,0 +1,326 @@ +/* Test suite for the library. First, it ``tests'' that all the constructs it + * uses compile successfully. Then, its output to stdout is compared to the + * expected output automatically extracted from slash-slash comments below. + * + * NOTE: For now, the test suite expects a 32-bit system. On others, some tests + * may fail, and it may be ineffective at catching bugs. TODO: Remedy this. */ + +#include "BigIntegerLibrary.hh" + +#include +#include +using namespace std; + +// Evaluate expr and print the result or "error" as appropriate. +#define TEST(expr) do {\ + cout << "Line " << __LINE__ << ": ";\ + try {\ + cout << (expr);\ + } catch (const char *err) {\ + cout << "error";\ + }\ + cout << endl;\ +} while (0) + +const BigUnsigned &check(const BigUnsigned &x) { + unsigned int l = x.getLength(); + if (l != 0 && x.getBlock(l-1) == 0) + cout << "check: Unzapped number!" << endl; + if (l > x.getCapacity()) + cout << "check: Capacity inconsistent with length!" << endl; + return x; +} + +const BigInteger &check(const BigInteger &x) { + if (x.getSign() == 0 && !x.getMagnitude().isZero()) + cout << "check: Sign should not be zero!" << endl; + if (x.getSign() != 0 && x.getMagnitude().isZero()) + cout << "check: Sign should be zero!" << endl; + check(x.getMagnitude()); + return x; +} + +short pathologicalShort = ~((unsigned short)(~0) >> 1); +int pathologicalInt = ~((unsigned int)(~0) >> 1); +long pathologicalLong = ~((unsigned long)(~0) >> 1); + +int main() { + +try { + +BigUnsigned z(0), one(1), ten(10); +TEST(z); //0 +TEST(1); //1 +TEST(10); //10 + +// TODO: Comprehensively test the general and special cases of each function. + +// === Default constructors === + +TEST(check(BigUnsigned())); //0 +TEST(check(BigInteger())); //0 + +// === Block-array constructors === + +BigUnsigned::Blk myBlocks[3]; +myBlocks[0] = 3; +myBlocks[1] = 4; +myBlocks[2] = 0; +BigUnsigned bu(myBlocks, 3); +TEST(check(bu)); //17179869187 +TEST(check(BigInteger(myBlocks, 3))); //17179869187 +TEST(check(BigInteger(bu ))); //17179869187 + +// For nonzero magnitude, reject zero and invalid signs. +TEST(check(BigInteger(myBlocks, 3, BigInteger::positive))); //17179869187 +TEST(check(BigInteger(myBlocks, 3, BigInteger::negative))); //-17179869187 +TEST(check(BigInteger(myBlocks, 3, BigInteger::zero ))); //error +TEST(check(BigInteger(bu, BigInteger::positive))); //17179869187 +TEST(check(BigInteger(bu, BigInteger::negative))); //-17179869187 +TEST(check(BigInteger(bu, BigInteger::zero ))); //error + +// For zero magnitude, force the sign to zero without error. +BigUnsigned::Blk myZeroBlocks[1]; +myZeroBlocks[0] = 0; +TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::positive))); //0 +TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::negative))); //0 +TEST(check(BigInteger(myZeroBlocks, 1, BigInteger::zero ))); //0 + +// === BigUnsigned conversion limits === + +TEST(BigUnsigned(0).toUnsignedLong()); //0 +TEST(BigUnsigned(4294967295U).toUnsignedLong()); //4294967295 +TEST(stringToBigUnsigned("4294967296").toUnsignedLong()); //error + +TEST(BigUnsigned(0).toLong()); //0 +TEST(BigUnsigned(2147483647).toLong()); //2147483647 +TEST(BigUnsigned(2147483648U).toLong()); //error + +// int is the same as long on a 32-bit system +TEST(BigUnsigned(0).toUnsignedInt()); //0 +TEST(BigUnsigned(4294967295U).toUnsignedInt()); //4294967295 +TEST(stringToBigUnsigned("4294967296").toUnsignedInt()); //error + +TEST(BigUnsigned(0).toInt()); //0 +TEST(BigUnsigned(2147483647).toInt()); //2147483647 +TEST(BigUnsigned(2147483648U).toInt()); //error + +TEST(BigUnsigned(0).toUnsignedShort()); //0 +TEST(BigUnsigned(65535).toUnsignedShort()); //65535 +TEST(BigUnsigned(65536).toUnsignedShort()); //error + +TEST(BigUnsigned(0).toShort()); //0 +TEST(BigUnsigned(32767).toShort()); //32767 +TEST(BigUnsigned(32768).toShort()); //error + +// === BigInteger conversion limits === + +TEST(BigInteger(-1).toUnsignedLong()); //error +TEST(BigInteger(0).toUnsignedLong()); //0 +TEST(BigInteger(4294967295U).toUnsignedLong()); //4294967295 +TEST(stringToBigInteger("4294967296").toUnsignedLong()); //error + +TEST(stringToBigInteger("-2147483649").toLong()); //error +TEST(stringToBigInteger("-2147483648").toLong()); //-2147483648 +TEST(BigInteger(-2147483647).toLong()); //-2147483647 +TEST(BigInteger(0).toLong()); //0 +TEST(BigInteger(2147483647).toLong()); //2147483647 +TEST(BigInteger(2147483648U).toLong()); //error + +// int is the same as long on a 32-bit system +TEST(BigInteger(-1).toUnsignedInt()); //error +TEST(BigInteger(0).toUnsignedInt()); //0 +TEST(BigInteger(4294967295U).toUnsignedInt()); //4294967295 +TEST(stringToBigInteger("4294967296").toUnsignedInt()); //error + +TEST(stringToBigInteger("-2147483649").toInt()); //error +TEST(stringToBigInteger("-2147483648").toInt()); //-2147483648 +TEST(BigInteger(-2147483647).toInt()); //-2147483647 +TEST(BigInteger(0).toInt()); //0 +TEST(BigInteger(2147483647).toInt()); //2147483647 +TEST(BigInteger(2147483648U).toInt()); //error + +TEST(BigInteger(-1).toUnsignedShort()); //error +TEST(BigInteger(0).toUnsignedShort()); //0 +TEST(BigInteger(65535).toUnsignedShort()); //65535 +TEST(BigInteger(65536).toUnsignedShort()); //error + +TEST(BigInteger(-32769).toShort()); //error +TEST(BigInteger(-32768).toShort()); //-32768 +TEST(BigInteger(-32767).toShort()); //-32767 +TEST(BigInteger(0).toShort()); //0 +TEST(BigInteger(32767).toShort()); //32767 +TEST(BigInteger(32768).toShort()); //error + +// === Negative BigUnsigneds === + +// ...during construction +TEST(BigUnsigned(short(-1))); //error +TEST(BigUnsigned(pathologicalShort)); //error +TEST(BigUnsigned(-1)); //error +TEST(BigUnsigned(pathologicalInt)); //error +TEST(BigUnsigned(long(-1))); //error +TEST(BigUnsigned(pathologicalLong)); //error + +// ...during subtraction +TEST(BigUnsigned(5) - BigUnsigned(6)); //error +TEST(stringToBigUnsigned("314159265358979323") - stringToBigUnsigned("314159265358979324")); //error +TEST(check(BigUnsigned(5) - BigUnsigned(5))); //0 +TEST(check(stringToBigUnsigned("314159265358979323") - stringToBigUnsigned("314159265358979323"))); //0 +TEST(check(stringToBigUnsigned("4294967296") - BigUnsigned(1))); //4294967295 + +// === BigUnsigned addition === + +TEST(check(BigUnsigned(0) + 0)); //0 +TEST(check(BigUnsigned(0) + 1)); //1 +// Ordinary carry +TEST(check(stringToBigUnsigned("8589934591" /* 2^33 - 1*/) + + stringToBigUnsigned("4294967298" /* 2^32 + 2 */))); //12884901889 +// Creation of a new block +TEST(check(BigUnsigned(0xFFFFFFFFU) + 1)); //4294967296 + +// === BigUnsigned subtraction === + +TEST(check(BigUnsigned(1) - 0)); //1 +TEST(check(BigUnsigned(1) - 1)); //0 +TEST(check(BigUnsigned(2) - 1)); //1 +// Ordinary borrow +TEST(check(stringToBigUnsigned("12884901889") + - stringToBigUnsigned("4294967298"))); //8589934591 +// Borrow that removes a block +TEST(check(stringToBigUnsigned("4294967296") - 1)); //4294967295 + +// === BigUnsigned multiplication and division === + +BigUnsigned a = check(BigUnsigned(314159265) * 358979323); +TEST(a); //112776680263877595 +TEST(a / 123); //916883579381118 +TEST(a % 123); //81 + +TEST(BigUnsigned(5) / 0); //error + +// === Block accessors === + +BigUnsigned b; +TEST(b); //0 +TEST(b.getBlock(0)); //0 +b.setBlock(1, 314); +// Did b grow properly? And did we zero intermediate blocks? +TEST(check(b)); //1348619730944 +TEST(b.getLength()); //2 +TEST(b.getBlock(0)); //0 +TEST(b.getBlock(1)); //314 +// Did b shrink properly? +b.setBlock(1, 0); +TEST(check(b)); //0 + +BigUnsigned bb(314); +bb.setBlock(1, 159); +// Make sure we used allocateAndCopy, not allocate +TEST(bb.getBlock(0)); //314 +TEST(bb.getBlock(1)); //159 +// Blocks beyond the number should be zero regardless of whether they are +// within the capacity. +bb.add(1, 2); +TEST(bb.getBlock(0)); //3 +TEST(bb.getBlock(1)); //0 +TEST(bb.getBlock(2)); //0 +TEST(bb.getBlock(314159)); //0 + +// === Bit accessors === + +TEST(BigUnsigned(0).bitLength()); //0 +TEST(BigUnsigned(1).bitLength()); //1 +TEST(BigUnsigned(4095).bitLength()); //12 +TEST(BigUnsigned(4096).bitLength()); //13 +// 5 billion is between 2^32 (about 4 billion) and 2^33 (about 8 billion). +TEST(stringToBigUnsigned("5000000000").bitLength()); //33 + +// 25 is binary 11001. +BigUnsigned bbb(25); +TEST(bbb.getBit(4)); //1 +TEST(bbb.getBit(3)); //1 +TEST(bbb.getBit(2)); //0 +TEST(bbb.getBit(1)); //0 +TEST(bbb.getBit(0)); //1 +TEST(bbb.bitLength()); //5 +// Effectively add 2^32. +bbb.setBit(32, true); +TEST(bbb); //4294967321 +bbb.setBit(31, true); +bbb.setBit(32, false); +TEST(check(bbb)); //2147483673 + +// === Combining BigUnsigned, BigInteger, and primitive integers === + +BigUnsigned p1 = BigUnsigned(3) * 5; +TEST(p1); //15 +/* In this case, we would like g++ to implicitly promote the BigUnsigned to a + * BigInteger, but it seems to prefer converting the -5 to a BigUnsigned, which + * causes an error. If I take out constructors for BigUnsigned from signed + * primitive integers, the BigUnsigned(3) becomes ambiguous, and if I take out + * all the constructors but BigUnsigned(unsigned long), g++ uses that + * constructor and gets a wrong (positive) answer. Thus, I think we'll just + * have to live with this cast. */ +BigInteger p2 = BigInteger(BigUnsigned(3)) * -5; +TEST(p2); //-15 + +// === Test some previous bugs === + +{ + /* Test that BigInteger division sets the sign to zero. + * Bug reported by David Allen. */ + BigInteger num(3), denom(5), quotient; + num.divideWithRemainder(denom, quotient); + check(quotient); + num = 5; + num.divideWithRemainder(denom, quotient); + check(num); +} + +{ + /* Test that BigInteger subtraction sets the sign properly. + * Bug reported by Samuel Larkin. */ + BigInteger zero(0), three(3), ans; + ans = zero - three; + TEST(check(ans).getSign()); //-1 +} + +{ + /* Test that BigInteger multiplication shifts bits properly on systems + * where long is bigger than int. (Obviously, this would only catch the + * bug when run on such a system.) + * Bug reported by Mohand Mezmaz. */ + BigInteger f=4; f*=3; + TEST(check(f)); //12 +} + +{ + /* Test that bitwise XOR allocates the larger length. + * Bug reported by Sriram Sankararaman. */ + BigUnsigned a(0), b(3), ans; + ans = a ^ b; + TEST(ans); //3 +} + +{ + /* Test that an aliased multiplication works. + * Bug reported by Boris Dessy. */ + BigInteger num(5); + num *= num; + TEST(check(num)); //25 +} + +{ + /* Test that BigUnsignedInABase(std::string) constructor rejects digits + * too big for the specified base. + * Bug reported by Niakam Kazemi. */ + TEST(BigUnsignedInABase("f", 10)); //error +} + +} catch (const char *err) { + cout << "UNCAUGHT ERROR: " << err << endl; +} + +return 0; +} diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc new file mode 100644 index 000000000..993ead928 --- /dev/null +++ b/frontends/ast/Makefile.inc @@ -0,0 +1,5 @@ + +OBJS += frontends/ast/ast.o +OBJS += frontends/ast/simplify.o +OBJS += frontends/ast/genrtlil.o + diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc new file mode 100644 index 000000000..160e9c429 --- /dev/null +++ b/frontends/ast/ast.cc @@ -0,0 +1,859 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This is the AST frontend library. + * + * The AST frontend library is not a frontend on it's own but provides a + * generic abstract syntax tree (AST) abstraction for HDL code and can be + * used by HDL frontends. See "ast.h" for an overview of the API and the + * Verilog frontend for an usage example. + * + */ + +#include "kernel/log.h" +#include "kernel/sha1.h" +#include "ast.h" + +#include +#include +#include + +using namespace AST; +using namespace AST_INTERNAL; + +// instanciate global variables (public API) +namespace AST { + std::string current_filename; + void (*set_line_num)(int) = NULL; + int (*get_line_num)() = NULL; +} + +// instanciate global variables (private API) +namespace AST_INTERNAL { + bool flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg; + AstNode *current_ast, *current_ast_mod; + std::map current_scope; + RTLIL::SigSpec *genRTLIL_subst_from = NULL; + RTLIL::SigSpec *genRTLIL_subst_to = NULL; + AstNode *current_top_block, *current_block, *current_block_child; + AstModule *current_module; +} + +// convert node types to string +std::string AST::type2str(AstNodeType type) +{ + switch (type) + { +#define X(_item) case _item: return #_item; + X(AST_NONE) + X(AST_DESIGN) + X(AST_MODULE) + X(AST_TASK) + X(AST_FUNCTION) + X(AST_WIRE) + X(AST_MEMORY) + X(AST_AUTOWIRE) + X(AST_PARAMETER) + X(AST_LOCALPARAM) + X(AST_PARASET) + X(AST_ARGUMENT) + X(AST_RANGE) + X(AST_CONSTANT) + X(AST_CELLTYPE) + X(AST_IDENTIFIER) + X(AST_FCALL) + X(AST_TO_SIGNED) + X(AST_TO_UNSIGNED) + X(AST_CONCAT) + X(AST_REPLICATE) + X(AST_BIT_NOT) + X(AST_BIT_AND) + X(AST_BIT_OR) + X(AST_BIT_XOR) + X(AST_BIT_XNOR) + X(AST_REDUCE_AND) + X(AST_REDUCE_OR) + X(AST_REDUCE_XOR) + X(AST_REDUCE_XNOR) + X(AST_REDUCE_BOOL) + X(AST_SHIFT_LEFT) + X(AST_SHIFT_RIGHT) + X(AST_SHIFT_SLEFT) + X(AST_SHIFT_SRIGHT) + X(AST_LT) + X(AST_LE) + X(AST_EQ) + X(AST_NE) + X(AST_GE) + X(AST_GT) + X(AST_ADD) + X(AST_SUB) + X(AST_MUL) + X(AST_DIV) + X(AST_MOD) + X(AST_POW) + X(AST_POS) + X(AST_NEG) + X(AST_LOGIC_AND) + X(AST_LOGIC_OR) + X(AST_LOGIC_NOT) + X(AST_TERNARY) + X(AST_MEMRD) + X(AST_MEMWR) + X(AST_TCALL) + X(AST_ASSIGN) + X(AST_CELL) + X(AST_PRIMITIVE) + X(AST_ALWAYS) + X(AST_BLOCK) + X(AST_ASSIGN_EQ) + X(AST_ASSIGN_LE) + X(AST_CASE) + X(AST_COND) + X(AST_DEFAULT) + X(AST_FOR) + X(AST_GENVAR) + X(AST_GENFOR) + X(AST_GENIF) + X(AST_GENBLOCK) + X(AST_POSEDGE) + X(AST_NEGEDGE) + X(AST_EDGE) +#undef X + default: + assert(!"Missing enum to string def in AST::type2str()."); + abort(); + } +} + +// create new node (AstNode constructor) +// (the optional child arguments make it easier to create AST trees) +AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2) +{ + this->type = type; + filename = current_filename; + linenum = get_line_num(); + is_input = false; + is_output = false; + is_reg = false; + is_signed = false; + range_valid = false; + port_id = 0; + range_left = -1; + range_right = 0; + integer = 0; + id2ast = NULL; + + if (child1) + children.push_back(child1); + if (child2) + children.push_back(child2); +} + +// create a (deep recursive) copy of a node +AstNode *AstNode::clone() +{ + AstNode *that = new AstNode; + *that = *this; + for (auto &it : that->children) + it = it->clone(); + for (auto &it : that->attributes) + it.second = it.second->clone(); + return that; +} + +// create a (deep recursive) copy of a node use 'other' as target root node +void AstNode::cloneInto(AstNode *other) +{ + AstNode *tmp = clone(); + other->delete_children(); + *other = *tmp; + tmp->children.clear(); + tmp->attributes.clear(); + delete tmp; +} + +// delete all children in this node +void AstNode::delete_children() +{ + for (auto &it : children) + delete it; + children.clear(); + + for (auto &it : attributes) + delete it.second; + attributes.clear(); +} + +// AstNode destructor +AstNode::~AstNode() +{ + delete_children(); +} + +// create a nice text representation of the node +// (traverse tree by recursion, use 'other' pointer for diffing two AST trees) +void AstNode::dumpAst(FILE *f, std::string indent, AstNode *other) +{ + if (f == NULL) { + for (auto f : log_files) + dumpAst(f, indent, other); + return; + } + if (other != NULL) { + if (type != other->type) + goto found_diff_to_other; + if (children.size() != other->children.size()) + goto found_diff_to_other; + if (str != other->str) + goto found_diff_to_other; + if (bits != other->bits) + goto found_diff_to_other; + if (is_input != other->is_input) + goto found_diff_to_other; + if (is_output != other->is_output) + goto found_diff_to_other; + if (is_reg != other->is_reg) + goto found_diff_to_other; + if (is_signed != other->is_signed) + goto found_diff_to_other; + if (range_valid != other->range_valid) + goto found_diff_to_other; + if (port_id != other->port_id) + goto found_diff_to_other; + if (range_left != other->range_left) + goto found_diff_to_other; + if (range_right != other->range_right) + goto found_diff_to_other; + if (integer != other->integer) + goto found_diff_to_other; + if (0) { + found_diff_to_other: + other->dumpAst(f, indent + "- "); + this->dumpAst(f, indent + "+ "); + return; + } + } + + std::string type_name = type2str(type); + fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum); + if (!str.empty()) + fprintf(f, " str='%s'", str.c_str()); + if (!bits.empty()) { + fprintf(f, " bits='"); + for (size_t i = bits.size(); i > 0; i--) + fprintf(f, "%c", bits[i-1] == RTLIL::S0 ? '0' : + bits[i-1] == RTLIL::S1 ? '1' : + bits[i-1] == RTLIL::Sx ? 'x' : + bits[i-1] == RTLIL::Sz ? 'z' : '?'); + fprintf(f, "'(%zd)", bits.size()); + } + if (is_input) + fprintf(f, " input"); + if (is_output) + fprintf(f, " output"); + if (is_reg) + fprintf(f, " reg"); + if (is_signed) + fprintf(f, " signed"); + if (port_id > 0) + fprintf(f, " port=%d", port_id); + if (range_valid || range_left != -1 || range_right != 0) + fprintf(f, " range=[%d:%d]%s", range_left, range_right, range_valid ? "" : "!"); + if (integer != 0) + fprintf(f, " int=%u", (int)integer); + fprintf(f, "\n"); + + for (size_t i = 0; i < children.size(); i++) + children[i]->dumpAst(f, indent + " ", other ? other->children[i] : NULL); +} + +// helper function for AstNode::dumpVlog() +static std::string id2vl(std::string txt) +{ + if (txt.size() > 1 && txt[0] == '\\') + txt = txt.substr(1); + for (size_t i = 0; i < txt.size(); i++) { + if ('A' <= txt[i] && txt[i] <= 'Z') continue; + if ('a' <= txt[i] && txt[i] <= 'z') continue; + if ('0' <= txt[i] && txt[i] <= '9') continue; + if (txt[i] == '_') continue; + txt = "\\" + txt + " "; + break; + } + return txt; +} + +// dump AST node as verilog pseudo-code +void AstNode::dumpVlog(FILE *f, std::string indent) +{ + bool first = true; + std::string txt; + std::vector rem_children1, rem_children2; + + if (f == NULL) { + for (auto f : log_files) + dumpVlog(f, indent); + return; + } + + switch (type) + { + case AST_MODULE: + fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str()); + for (auto child : children) + if (child->type == AST_WIRE && (child->is_input || child->is_output)) { + fprintf(f, "%s%s", first ? "" : ", ", id2vl(child->str).c_str()); + first = false; + } + fprintf(f, ");\n"); + + for (auto child : children) + if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) + child->dumpVlog(f, indent + " "); + else + rem_children1.push_back(child); + + for (auto child : rem_children1) + if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY) + child->dumpVlog(f, indent + " "); + else + rem_children2.push_back(child); + rem_children1.clear(); + + for (auto child : rem_children2) + if (child->type == AST_TASK || child->type == AST_FUNCTION) + child->dumpVlog(f, indent + " "); + else + rem_children1.push_back(child); + rem_children2.clear(); + + for (auto child : rem_children1) + child->dumpVlog(f, indent + " "); + rem_children1.clear(); + + fprintf(f, "%s" "endmodule\n", indent.c_str()); + break; + + case AST_WIRE: + if (is_input && is_output) + fprintf(f, "%s" "inout", indent.c_str()); + else if (is_input) + fprintf(f, "%s" "input", indent.c_str()); + else if (is_output) + fprintf(f, "%s" "output", indent.c_str()); + else if (!is_reg) + fprintf(f, "%s" "wire", indent.c_str()); + if (is_reg) + fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str()); + if (is_signed) + fprintf(f, " signed"); + for (auto child : children) { + fprintf(f, " "); + child->dumpVlog(f, ""); + } + fprintf(f, " %s", id2vl(str).c_str()); + fprintf(f, ";\n"); + break; + + case AST_MEMORY: + fprintf(f, "%s" "memory", indent.c_str()); + if (is_signed) + fprintf(f, " signed"); + for (auto child : children) { + fprintf(f, " "); + child->dumpVlog(f, ""); + if (first) + fprintf(f, " %s", id2vl(str).c_str()); + first = false; + } + fprintf(f, ";\n"); + break; + + case AST_RANGE: + if (range_valid) + fprintf(f, "[%d:%d]", range_left, range_right); + else { + for (auto child : children) { + fprintf(f, "%c", first ? '[' : ':'); + child->dumpVlog(f, ""); + first = false; + } + fprintf(f, "]"); + } + break; + + case AST_ALWAYS: + fprintf(f, "%s" "always @(", indent.c_str()); + for (auto child : children) { + if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) + continue; + if (!first) + fprintf(f, ", "); + child->dumpVlog(f, ""); + first = false; + } + fprintf(f, ")\n"); + for (auto child : children) { + if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) + child->dumpVlog(f, indent + " "); + } + break; + + case AST_POSEDGE: + case AST_NEGEDGE: + case AST_EDGE: + if (type == AST_POSEDGE) + fprintf(f, "posedge "); + if (type == AST_NEGEDGE) + fprintf(f, "negedge "); + for (auto child : children) + child->dumpVlog(f, ""); + break; + + case AST_IDENTIFIER: + fprintf(f, "%s", id2vl(str).c_str()); + for (auto child : children) + child->dumpVlog(f, ""); + break; + + case AST_CONSTANT: + if (!str.empty()) + fprintf(f, "\"%s\"", str.c_str()); + else if (bits.size() == 32) + fprintf(f, "%d", RTLIL::Const(bits).as_int()); + else + fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str()); + break; + + case AST_BLOCK: + if (children.size() == 1) { + children[0]->dumpVlog(f, indent); + } else { + fprintf(f, "%s" "begin\n", indent.c_str()); + for (auto child : children) + child->dumpVlog(f, indent + " "); + fprintf(f, "%s" "end\n", indent.c_str()); + } + break; + + case AST_CASE: + fprintf(f, "%s" "case (", indent.c_str()); + children[0]->dumpVlog(f, ""); + fprintf(f, ")\n"); + for (size_t i = 1; i < children.size(); i++) { + AstNode *child = children[i]; + child->dumpVlog(f, indent + " "); + } + fprintf(f, "%s" "endcase\n", indent.c_str()); + break; + + case AST_COND: + for (auto child : children) { + if (child->type == AST_BLOCK) { + fprintf(f, ":\n"); + child->dumpVlog(f, indent + " "); + first = true; + } else { + fprintf(f, "%s", first ? indent.c_str() : ", "); + if (child->type == AST_DEFAULT) + fprintf(f, "default"); + else + child->dumpVlog(f, ""); + first = false; + } + } + break; + + case AST_ASSIGN_EQ: + case AST_ASSIGN_LE: + fprintf(f, "%s", indent.c_str()); + children[0]->dumpVlog(f, ""); + fprintf(f, " %s ", type == AST_ASSIGN_EQ ? "=" : "<="); + children[1]->dumpVlog(f, ""); + fprintf(f, ";\n"); + break; + + case AST_CONCAT: + fprintf(f, "{"); + for (auto child : children) { + if (!first) + fprintf(f, ", "); + child->dumpVlog(f, ""); + first = false; + } + fprintf(f, "}"); + break; + + case AST_REPLICATE: + fprintf(f, "{"); + children[0]->dumpVlog(f, ""); + fprintf(f, "{"); + children[1]->dumpVlog(f, ""); + fprintf(f, "}}"); + break; + + if (0) { case AST_BIT_NOT: txt = "~"; } + if (0) { case AST_REDUCE_AND: txt = "&"; } + if (0) { case AST_REDUCE_OR: txt = "|"; } + if (0) { case AST_REDUCE_XOR: txt = "^"; } + if (0) { case AST_REDUCE_XNOR: txt = "~^"; } + if (0) { case AST_REDUCE_BOOL: txt = "|"; } + if (0) { case AST_POS: txt = "+"; } + if (0) { case AST_NEG: txt = "-"; } + if (0) { case AST_LOGIC_NOT: txt = "!"; } + fprintf(f, "%s(", txt.c_str()); + children[0]->dumpVlog(f, ""); + fprintf(f, ")"); + break; + + if (0) { case AST_BIT_AND: txt = "&"; } + if (0) { case AST_BIT_OR: txt = "|"; } + if (0) { case AST_BIT_XOR: txt = "^"; } + if (0) { case AST_BIT_XNOR: txt = "~^"; } + if (0) { case AST_SHIFT_LEFT: txt = "<<"; } + if (0) { case AST_SHIFT_RIGHT: txt = ">>"; } + if (0) { case AST_SHIFT_SLEFT: txt = "<<<"; } + if (0) { case AST_SHIFT_SRIGHT: txt = ">>>"; } + if (0) { case AST_LT: txt = "<"; } + if (0) { case AST_LE: txt = "<="; } + if (0) { case AST_EQ: txt = "=="; } + if (0) { case AST_NE: txt = "!="; } + if (0) { case AST_GE: txt = ">="; } + if (0) { case AST_GT: txt = ">"; } + if (0) { case AST_ADD: txt = "+"; } + if (0) { case AST_SUB: txt = "-"; } + if (0) { case AST_MUL: txt = "*"; } + if (0) { case AST_DIV: txt = "/"; } + if (0) { case AST_MOD: txt = "%"; } + if (0) { case AST_POW: txt = "**"; } + if (0) { case AST_LOGIC_AND: txt = "&&"; } + if (0) { case AST_LOGIC_OR: txt = "||"; } + fprintf(f, "("); + children[0]->dumpVlog(f, ""); + fprintf(f, ")%s(", txt.c_str()); + children[1]->dumpVlog(f, ""); + fprintf(f, ")"); + break; + + case AST_TERNARY: + fprintf(f, "("); + children[0]->dumpVlog(f, ""); + fprintf(f, ") ? ("); + children[1]->dumpVlog(f, ""); + fprintf(f, ") : ("); + children[2]->dumpVlog(f, ""); + fprintf(f, ")"); + break; + + default: + std::string type_name = type2str(type); + fprintf(f, "%s" "/** %s **/%s", indent.c_str(), type_name.c_str(), indent.empty() ? "" : "\n"); + // dumpAst(f, indent, NULL); + } +} + +// check if two AST nodes are identical +bool AstNode::operator==(const AstNode &other) const +{ + if (type != other.type) + return false; + if (children.size() != other.children.size()) + return false; + if (str != other.str) + return false; + if (bits != other.bits) + return false; + if (is_input != other.is_input) + return false; + if (is_output != other.is_output) + return false; + if (is_reg != other.is_reg) + return false; + if (is_signed != other.is_signed) + return false; + if (range_valid != other.range_valid) + return false; + if (port_id != other.port_id) + return false; + if (range_left != other.range_left) + return false; + if (range_right != other.range_right) + return false; + if (integer != other.integer) + return false; + for (size_t i = 0; i < children.size(); i++) + if (*children[i] != *other.children[i]) + return false; + return true; +} + +// check if two AST nodes are not identical +bool AstNode::operator!=(const AstNode &other) const +{ + return !(*this == other); +} + +// check if this AST contains the given node +bool AstNode::contains(const AstNode *other) const +{ + if (this == other) + return true; + for (auto child : children) + if (child->contains(other)) + return true; + return false; +} + +// create an AST node for a constant (using a 32 bit int as value) +AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width) +{ + AstNode *node = new AstNode(AST_CONSTANT); + node->integer = v; + node->is_signed = is_signed; + for (int i = 0; i < width; i++) { + node->bits.push_back((v & 1) ? RTLIL::S1 : RTLIL::S0); + v = v >> 1; + } + node->range_valid = true; + node->range_left = width-1; + node->range_right = 0; + return node; +} + +// create an AST node for a constant (using a bit vector as value) +AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signed) +{ + AstNode *node = new AstNode(AST_CONSTANT); + node->is_signed = is_signed; + node->bits = v; + for (size_t i = 0; i < 32; i++) { + if (i < node->bits.size()) + node->integer |= (node->bits[i] == RTLIL::S1) << i; + else if (is_signed) + node->integer |= (node->bits.back() == RTLIL::S1) << i; + } + node->range_valid = true; + node->range_left = node->bits.size(); + node->range_right = 0; + return node; +} + +// create a new AstModule from an AST_MODULE AST node +static AstModule* process_module(AstNode *ast) +{ + assert(ast->type == AST_MODULE); + log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); + + current_ast_mod = ast; + AstNode *ast_before_simplify = ast->clone(); + + while (ast->simplify(false, false, false, 0)) { } + + if (flag_dump_ast) { + log("Dumping verilog AST (as requested by %s option):\n", flag_dump_ast_diff ? "dump_ast_diff" : "dump_ast"); + ast->dumpAst(NULL, " ", flag_dump_ast_diff ? ast_before_simplify : NULL); + log("--- END OF AST DUMP ---\n"); + } + + if (flag_dump_vlog) { + log("Dumping verilog AST (as requested by dump_vlog option):\n"); + ast->dumpVlog(NULL, " "); + log("--- END OF AST DUMP ---\n"); + } + + current_module = new AstModule; + current_module->ast = NULL; + current_module->name = ast->str; + current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum); + for (auto &attr : ast->attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), ast->filename.c_str(), ast->linenum); + current_module->attributes[attr.first].str = attr.second->str; + current_module->attributes[attr.first].bits = attr.second->bits; + } + for (size_t i = 0; i < ast->children.size(); i++) { + AstNode *node = ast->children[i]; + if (node->type == AST_WIRE || node->type == AST_MEMORY) + node->genRTLIL(); + } + for (size_t i = 0; i < ast->children.size(); i++) { + AstNode *node = ast->children[i]; + if (node->type != AST_WIRE && node->type != AST_MEMORY) + node->genRTLIL(); + } + + current_module->ast = ast_before_simplify; + current_module->nolatches = flag_nolatches; + current_module->nomem2reg = flag_nomem2reg; + return current_module; +} + +// create AstModule instances for all modules in the AST tree and add them to 'design' +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast, bool dump_ast_diff, bool dump_vlog, bool nolatches, bool nomem2reg) +{ + current_ast = ast; + flag_dump_ast = dump_ast; + flag_dump_ast_diff = dump_ast_diff; + flag_dump_vlog = dump_vlog; + flag_nolatches = nolatches; + flag_nomem2reg = nomem2reg; + + assert(current_ast->type == AST_DESIGN); + for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { + if (design->modules.count((*it)->str) != 0) + log_error("Re-definition of module `%s' at %s:%d!\n", + (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); + design->modules[(*it)->str] = process_module(*it); + } +} + +// AstModule destructor +AstModule::~AstModule() +{ + if (ast != NULL) + delete ast; +} + +// create a new parametric module (when needed) and return the name of the generated module +RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map parameters) +{ + log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", name.c_str()); + + current_ast = NULL; + flag_dump_ast = false; + flag_dump_ast_diff = false; + flag_dump_vlog = false; + flag_nolatches = nolatches; + flag_nomem2reg = nomem2reg; + use_internal_line_num(); + + std::vector hash_data; + hash_data.insert(hash_data.end(), name.begin(), name.end()); + hash_data.push_back(0); + + AstNode *new_ast = ast->clone(); + + int para_counter = 0; + for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) { + AstNode *child = *it; + if (child->type != AST_PARAMETER) + continue; + para_counter++; + std::string para_id = child->str; + if (parameters.count(child->str) > 0) { + log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); + rewrite_parameter: + child->delete_children(); + child->children.push_back(AstNode::mkconst_bits(parameters[para_id].bits, false)); + hash_data.insert(hash_data.end(), child->str.begin(), child->str.end()); + hash_data.push_back(0); + hash_data.insert(hash_data.end(), parameters[para_id].bits.begin(), parameters[para_id].bits.end()); + hash_data.push_back(0xff); + parameters.erase(para_id); + continue; + } + char buf[100]; + snprintf(buf, 100, "$%d", para_counter); + if (parameters.count(buf) > 0) { + para_id = buf; + log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); + goto rewrite_parameter; + } + } + if (parameters.size() > 0) + log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), name.c_str()); + + unsigned char hash[20]; + unsigned char *hash_data2 = new unsigned char[hash_data.size()]; + for (size_t i = 0; i < hash_data.size(); i++) + hash_data2[i] = hash_data[i]; + sha1::calc(hash_data2, hash_data.size(), hash); + delete[] hash_data2; + + char hexstring[41]; + sha1::toHexString(hash, hexstring); + + std::string modname = "$paramod$" + std::string(hexstring) + "$" + name; + + if (design->modules.count(modname) == 0) { + new_ast->str = modname; + design->modules[modname] = process_module(new_ast); + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + +// recompile a module from AST with updated widths for auto-wires +// (auto-wires are wires that are used but not declared an thus have an automatically determined width) +void AstModule::update_auto_wires(std::map auto_sizes) +{ + log_header("Executing AST frontend in update_auto_wires mode using pre-parsed AST for module `%s'.\n", name.c_str()); + + current_ast = NULL; + flag_dump_ast = false; + flag_dump_ast_diff = false; + flag_dump_vlog = false; + flag_nolatches = nolatches; + flag_nomem2reg = nomem2reg; + use_internal_line_num(); + + for (auto it = auto_sizes.begin(); it != auto_sizes.end(); it++) { + log("Adding extra wire declaration to AST: wire [%d:0] %s\n", it->second - 1, it->first.c_str()); + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(it->second - 1, true), AstNode::mkconst_int(0, true))); + wire->str = it->first; + ast->children.insert(ast->children.begin(), wire); + } + + AstModule *newmod = process_module(ast); + + delete ast; + ast = newmod->ast; + newmod->ast = NULL; + + wires.swap(newmod->wires); + cells.swap(newmod->cells); + processes.swap(newmod->processes); + connections.swap(newmod->connections); + attributes.swap(newmod->attributes); + delete newmod; +} + +// internal dummy line number callbacks +namespace { + int internal_line_num; + void internal_set_line_num(int n) { + internal_line_num = n; + } + int internal_get_line_num() { + return internal_line_num; + } +} + +// use internal dummy line number callbacks +void AST::use_internal_line_num() +{ + set_line_num = &internal_set_line_num; + get_line_num = &internal_get_line_num; +} + diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h new file mode 100644 index 000000000..f7c9328c2 --- /dev/null +++ b/frontends/ast/ast.h @@ -0,0 +1,228 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This is the AST frontend library. + * + * The AST frontend library is not a frontend on it's own but provides a + * generic abstract syntax tree (AST) abstraction for HDL code and can be + * used by HDL frontends. See "ast.h" for an overview of the API and the + * Verilog frontend for an usage example. + * + */ + +#ifndef AST_H +#define AST_H + +#include "kernel/rtlil.h" +#include +#include + +namespace AST +{ + // all node types, type2str() must be extended + // whenever a new node type is added here + enum AstNodeType + { + AST_NONE, + AST_DESIGN, + AST_MODULE, + AST_TASK, + AST_FUNCTION, + + AST_WIRE, + AST_MEMORY, + AST_AUTOWIRE, + AST_PARAMETER, + AST_LOCALPARAM, + AST_PARASET, + AST_ARGUMENT, + AST_RANGE, + AST_CONSTANT, + AST_CELLTYPE, + AST_IDENTIFIER, + + AST_FCALL, + AST_TO_SIGNED, + AST_TO_UNSIGNED, + AST_CONCAT, + AST_REPLICATE, + AST_BIT_NOT, + AST_BIT_AND, + AST_BIT_OR, + AST_BIT_XOR, + AST_BIT_XNOR, + AST_REDUCE_AND, + AST_REDUCE_OR, + AST_REDUCE_XOR, + AST_REDUCE_XNOR, + AST_REDUCE_BOOL, + AST_SHIFT_LEFT, + AST_SHIFT_RIGHT, + AST_SHIFT_SLEFT, + AST_SHIFT_SRIGHT, + AST_LT, + AST_LE, + AST_EQ, + AST_NE, + AST_GE, + AST_GT, + AST_ADD, + AST_SUB, + AST_MUL, + AST_DIV, + AST_MOD, + AST_POW, + AST_POS, + AST_NEG, + AST_LOGIC_AND, + AST_LOGIC_OR, + AST_LOGIC_NOT, + AST_TERNARY, + AST_MEMRD, + AST_MEMWR, + + AST_TCALL, + AST_ASSIGN, + AST_CELL, + AST_PRIMITIVE, + AST_ALWAYS, + AST_BLOCK, + AST_ASSIGN_EQ, + AST_ASSIGN_LE, + AST_CASE, + AST_COND, + AST_DEFAULT, + AST_FOR, + + AST_GENVAR, + AST_GENFOR, + AST_GENIF, + AST_GENBLOCK, + + AST_POSEDGE, + AST_NEGEDGE, + AST_EDGE + }; + + // convert an node type to a string (e.g. for debug output) + std::string type2str(AstNodeType type); + + // The AST is built using instances of this struct + struct AstNode + { + // this nodes type + AstNodeType type; + + // the list of child nodes for this node + std::vector children; + + // the list of attributes assigned to this node + std::map attributes; + + // node content - most of it is unused in most node types + std::string str; + std::vector bits; + bool is_input, is_output, is_reg, is_signed, range_valid; + int port_id, range_left, range_right; + uint32_t integer; + + // this is set by simplify and used during RTLIL generation + AstNode *id2ast; + + // this is the original sourcecode location that resulted in this AST node + // it is automatically set by the constructor using AST::current_filename and + // the AST::get_line_num() callback function. + std::string filename; + int linenum; + + // creating and deleting nodes + AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL); + AstNode *clone(); + void cloneInto(AstNode *other); + void delete_children(); + ~AstNode(); + + // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. + // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() + bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage); + void expand_genblock(std::string index_var, std::string prefix, std::map &name_map); + void replace_ids(std::map &rules); + void mem2reg_as_needed_pass1(std::set &mem2reg_set, std::set &mem2reg_candidates, bool sync_proc, bool async_proc); + void mem2reg_as_needed_pass2(std::set &mem2reg_set, AstNode *mod, AstNode *block); + void meminfo(int &mem_width, int &mem_size, int &addr_bits); + + // create a human-readable text representation of the AST (for debugging) + void dumpAst(FILE *f, std::string indent, AstNode *other = NULL); + void dumpVlog(FILE *f, std::string indent); + + // create RTLIL code for this AST node + // for expressions the resulting signal vector is returned + // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module + RTLIL::SigSpec genRTLIL(int width_hint = -1); + RTLIL::SigSpec genWidthRTLIL(int width, RTLIL::SigSpec *subst_from = NULL, RTLIL::SigSpec *subst_to = NULL); + + // compare AST nodes + bool operator==(const AstNode &other) const; + bool operator!=(const AstNode &other) const; + bool contains(const AstNode *other) const; + + // helper functions for creating AST nodes for constants + static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32); + static AstNode *mkconst_bits(const std::vector &v, bool is_signed); + }; + + // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast = false, bool dump_ast_diff = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false); + + // parametric modules are supported directly by the AST library + // therfore we need our own derivate of RTLIL::Module with overloaded virtual functions + struct AstModule : RTLIL::Module { + AstNode *ast; + bool nolatches, nomem2reg; + virtual ~AstModule(); + virtual RTLIL::IdString derive(RTLIL::Design *design, std::map parameters); + virtual void update_auto_wires(std::map auto_sizes); + }; + + // this must be set by the language frontend before parsing the sources + // the AstNode constructor then uses current_filename and get_line_num() + // to initialize the filename and linenum properties of new nodes + extern std::string current_filename; + extern void (*set_line_num)(int); + extern int (*get_line_num)(); + + // set set_line_num and get_line_num to internal dummy functions + // (done by simplify(), AstModule::derive and AstModule::update_auto_wires to control + // the filename and linenum properties of new nodes not generated by a frontend parser) + void use_internal_line_num(); +} + +namespace AST_INTERNAL +{ + // internal state variables + extern bool flag_dump_ast, flag_dump_ast_diff, flag_nolatches, flag_nomem2reg; + extern AST::AstNode *current_ast, *current_ast_mod; + extern std::map current_scope; + extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to; + extern AST::AstNode *current_top_block, *current_block, *current_block_child; + extern AST::AstModule *current_module; + struct ProcessGenerator; +} + +#endif diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc new file mode 100644 index 000000000..9f1acb61a --- /dev/null +++ b/frontends/ast/genrtlil.cc @@ -0,0 +1,1054 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This is the AST frontend library. + * + * The AST frontend library is not a frontend on it's own but provides a + * generic abstract syntax tree (AST) abstraction for HDL code and can be + * used by HDL frontends. See "ast.h" for an overview of the API and the + * Verilog frontend for an usage example. + * + */ + +#include "kernel/log.h" +#include "kernel/sha1.h" +#include "ast.h" + +#include +#include +#include + +using namespace AST; +using namespace AST_INTERNAL; + +// helper function for creating RTLIL code for unary operations +static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true) +{ + std::stringstream sstr; + sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + + RTLIL::Cell *cell = new RTLIL::Cell; + cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); + cell->name = sstr.str(); + cell->type = type; + current_module->cells[cell->name] = cell; + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); + wire->name = cell->name + "_Y"; + wire->width = result_width; + current_module->wires[wire->name] = wire; + + RTLIL::SigChunk chunk; + chunk.wire = wire; + chunk.width = wire->width; + chunk.offset = 0; + + RTLIL::SigSpec sig; + sig.chunks.push_back(chunk); + sig.width = chunk.width; + + if (gen_attributes) + for (auto &attr : that->attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), that->filename.c_str(), that->linenum); + cell->attributes[attr.first].str = attr.second->str; + cell->attributes[attr.first].bits = attr.second->bits; + } + + cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.width); + cell->connections["\\A"] = arg; + + cell->parameters["\\Y_WIDTH"] = result_width; + cell->connections["\\Y"] = sig; + return sig; +} + +// helper function for creating RTLIL code for binary operations +static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) +{ + std::stringstream sstr; + sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + + RTLIL::Cell *cell = new RTLIL::Cell; + cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); + cell->name = sstr.str(); + cell->type = type; + current_module->cells[cell->name] = cell; + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); + wire->name = cell->name + "_Y"; + wire->width = result_width; + current_module->wires[wire->name] = wire; + + RTLIL::SigChunk chunk; + chunk.wire = wire; + chunk.width = wire->width; + chunk.offset = 0; + + RTLIL::SigSpec sig; + sig.chunks.push_back(chunk); + sig.width = chunk.width; + + for (auto &attr : that->attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), that->filename.c_str(), that->linenum); + cell->attributes[attr.first].str = attr.second->str; + cell->attributes[attr.first].bits = attr.second->bits; + } + + cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed); + cell->parameters["\\B_SIGNED"] = RTLIL::Const(that->children[1]->is_signed); + + cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.width); + cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.width); + + cell->connections["\\A"] = left; + cell->connections["\\B"] = right; + + cell->parameters["\\Y_WIDTH"] = result_width; + cell->connections["\\Y"] = sig; + return sig; +} + +// helper function for creating RTLIL code for multiplexers +static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) +{ + assert(cond.width == 1); + + std::stringstream sstr; + sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + + RTLIL::Cell *cell = new RTLIL::Cell; + cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); + cell->name = sstr.str(); + cell->type = "$mux"; + current_module->cells[cell->name] = cell; + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); + wire->name = cell->name + "_Y"; + wire->width = left.width; + current_module->wires[wire->name] = wire; + + RTLIL::SigChunk chunk; + chunk.wire = wire; + chunk.width = wire->width; + chunk.offset = 0; + + RTLIL::SigSpec sig; + sig.chunks.push_back(chunk); + sig.width = chunk.width; + + for (auto &attr : that->attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), that->filename.c_str(), that->linenum); + cell->attributes[attr.first].str = attr.second->str; + cell->attributes[attr.first].bits = attr.second->bits; + } + + cell->parameters["\\WIDTH"] = RTLIL::Const(left.width); + + cell->connections["\\A"] = right; + cell->connections["\\B"] = left; + cell->connections["\\S"] = cond; + cell->connections["\\Y"] = sig; + + return sig; +} + +// helper class for converting AST always nodes to RTLIL processes +struct AST_INTERNAL::ProcessGenerator +{ + // input and output structures + AstNode *always; + RTLIL::Process *proc; + + // This always points to the RTLIL::CaseRule beeing filled at the moment + RTLIL::CaseRule *current_case; + + // This two variables contain the replacement pattern to be used in the right hand side + // of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right + // hand side of the 2nd assignment needs to be replace with the temporary signal holding + // the value assigned in the first assignment. So when the first assignement is processed + // the according information is appended to subst_rvalue_from and subst_rvalue_to. + RTLIL::SigSpec subst_rvalue_from, subst_rvalue_to; + + // This two variables contain the replacement pattern to be used in the left hand side + // of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar + // should not be connected to the signal foo. Instead it must be connected to the temporary + // signal that is used as input for the register that drives the signal foo. + RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to; + + // The code here generates a number of temprorary signal for each output register. This + // map helps generating nice numbered names for all this temporary signals. + std::map new_temp_count; + + ProcessGenerator(AstNode *always) : always(always) + { + // generate process and simple root case + proc = new RTLIL::Process; + proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, RTLIL::autoidx++); + for (auto &attr : always->attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), always->filename.c_str(), always->linenum); + proc->attributes[attr.first].str = attr.second->str; + proc->attributes[attr.first].bits = attr.second->bits; + } + current_module->processes[proc->name] = proc; + current_case = &proc->root_case; + + // create initial temporary signal for all output registers + collect_lvalues(subst_lvalue_from, always, true, true); + subst_lvalue_to = new_temp_signal(subst_lvalue_from); + + bool found_anyedge_syncs = false; + for (auto child : always->children) + if (child->type == AST_EDGE) + found_anyedge_syncs = true; + + if (found_anyedge_syncs) { + log("Note: Assuming pure combinatorial block at %s:%d in\n", always->filename.c_str(), always->linenum); + log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n"); + log("use of @* instead of @(...) for better match of synthesis and simulation.\n"); + } + + // create syncs for the process + bool found_clocked_sync = false; + for (auto child : always->children) + if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) { + found_clocked_sync = true; + if (found_anyedge_syncs) + log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum); + RTLIL::SyncRule *syncrule = new RTLIL::SyncRule; + syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn; + syncrule->signal = child->children[0]->genRTLIL(); + addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to); + proc->syncs.push_back(syncrule); + } + if (proc->syncs.empty()) { + RTLIL::SyncRule *syncrule = new RTLIL::SyncRule; + syncrule->type = RTLIL::STa; + syncrule->signal = RTLIL::SigSpec(); + addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to); + proc->syncs.push_back(syncrule); + } + + // create initial assignments for the temporary signals + if ((flag_nolatches || always->attributes.count("\\nolatches") > 0 || current_module->attributes.count("\\nolatches")) && !found_clocked_sync) { + subst_rvalue_from = subst_lvalue_from; + subst_rvalue_to = RTLIL::SigSpec(RTLIL::State::Sx, subst_rvalue_from.width); + } else { + addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from); + } + + // process the AST + for (auto child : always->children) + if (child->type == AST_BLOCK) + processAst(child); + } + + // create new temporary signals + RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig) + { + sig.optimize(); + for (size_t i = 0; i < sig.chunks.size(); i++) + { + RTLIL::SigChunk &chunk = sig.chunks[i]; + if (chunk.wire == NULL) + continue; + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum); + do { + wire->name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++, + chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);; + } while (current_module->wires.count(wire->name) > 0); + wire->width = chunk.width; + current_module->wires[wire->name] = wire; + + chunk.wire = wire; + chunk.offset = 0; + } + return sig; + } + + // recursively traverse the AST an collect all assigned signals + void collect_lvalues(RTLIL::SigSpec ®, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true) + { + switch (ast->type) + { + case AST_CASE: + for (auto child : ast->children) + if (child != ast->children[0]) { + assert(child->type == AST_COND); + collect_lvalues(reg, child, type_eq, type_le, false); + } + break; + + case AST_COND: + case AST_ALWAYS: + for (auto child : ast->children) + if (child->type == AST_BLOCK) + collect_lvalues(reg, child, type_eq, type_le, false); + break; + + case AST_BLOCK: + for (auto child : ast->children) { + if (child->type == AST_ASSIGN_EQ && type_eq) + reg.append(child->children[0]->genRTLIL()); + if (child->type == AST_ASSIGN_LE && type_le) + reg.append(child->children[0]->genRTLIL()); + if (child->type == AST_CASE || child->type == AST_BLOCK) + collect_lvalues(reg, child, type_eq, type_le, false); + } + break; + + default: + assert(0); + } + + if (run_sort_and_unify) + reg.sort_and_unify(); + } + + // remove all assignments to the given signal pattern in a case and all its children + // when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this + // function is acalled to clean up the first two assignments as they are overwritten by + // the third assignment. + void removeSignalFromCaseTree(RTLIL::SigSpec pattern, RTLIL::CaseRule *cs) + { + for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) + it->first.remove2(pattern, &it->second); + + for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) + removeSignalFromCaseTree(pattern, *it2); + } + + // add an assignment (aka "action") but split it up in chunks. this way huge assignments + // are avoided and the generated $mux cells have a more "natural" size. + void addChunkActions(std::vector &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue) + { + assert(lvalue.width == rvalue.width); + lvalue.optimize(); + rvalue.optimize(); + + int offset = 0; + for (size_t i = 0; i < lvalue.chunks.size(); i++) { + RTLIL::SigSpec lhs = lvalue.chunks[i]; + RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue.chunks[i].width); + actions.push_back(RTLIL::SigSig(lhs, rhs)); + offset += lhs.width; + } + } + + // recursively process the AST and fill the RTLIL::Process + void processAst(AstNode *ast) + { + switch (ast->type) + { + case AST_BLOCK: + for (auto child : ast->children) + processAst(child); + break; + + case AST_ASSIGN_EQ: + case AST_ASSIGN_LE: + { + RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; + RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.width, &subst_rvalue_from, &subst_rvalue_to); + lvalue.replace(subst_lvalue_from, subst_lvalue_to); + + if (ast->type == AST_ASSIGN_EQ) { + subst_rvalue_from.remove2(unmapped_lvalue, &subst_rvalue_to); + subst_rvalue_from.append(unmapped_lvalue); + subst_rvalue_from.optimize(); + subst_rvalue_to.append(rvalue); + subst_rvalue_to.optimize(); + } + + removeSignalFromCaseTree(lvalue, current_case); + current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue)); + } + break; + + case AST_CASE: + { + RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; + sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_from, &subst_rvalue_to); + current_case->switches.push_back(sw); + + for (auto &attr : ast->attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), ast->filename.c_str(), ast->linenum); + sw->attributes[attr.first].str = attr.second->str; + sw->attributes[attr.first].bits = attr.second->bits; + } + + RTLIL::SigSpec this_case_eq_lvalue; + collect_lvalues(this_case_eq_lvalue, ast, true, false); + + RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue); + + RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue; + this_case_eq_rvalue.replace(subst_rvalue_from, subst_rvalue_to); + + RTLIL::SigSpec backup_subst_lvalue_from = subst_lvalue_from; + RTLIL::SigSpec backup_subst_lvalue_to = subst_lvalue_to; + + RTLIL::SigSpec backup_subst_rvalue_from = subst_rvalue_from; + RTLIL::SigSpec backup_subst_rvalue_to = subst_rvalue_to; + + bool generated_default_case = false; + RTLIL::CaseRule *last_generated_case = NULL; + for (auto child : ast->children) + { + if (child == ast->children[0] || generated_default_case) + continue; + assert(child->type == AST_COND); + + subst_lvalue_from = backup_subst_lvalue_from; + subst_lvalue_to = backup_subst_lvalue_to; + + subst_rvalue_from = backup_subst_rvalue_from; + subst_rvalue_to = backup_subst_rvalue_to; + + subst_lvalue_from.remove2(this_case_eq_lvalue, &subst_lvalue_to); + subst_lvalue_from.append(this_case_eq_lvalue); + subst_lvalue_from.optimize(); + subst_lvalue_to.append(this_case_eq_ltemp); + subst_lvalue_to.optimize(); + + RTLIL::CaseRule *backup_case = current_case; + current_case = new RTLIL::CaseRule; + last_generated_case = current_case; + addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); + for (auto node : child->children) { + if (node->type == AST_DEFAULT) { + generated_default_case = true; + current_case->compare.clear(); + } else if (node->type == AST_BLOCK) { + processAst(node); + } else if (!generated_default_case) + current_case->compare.push_back(node->genWidthRTLIL(sw->signal.width)); + } + sw->cases.push_back(current_case); + current_case = backup_case; + } + + if (last_generated_case != NULL && ast->attributes.count("\\full_case") > 0) { + last_generated_case->compare.clear(); + } else if (!generated_default_case) { + RTLIL::CaseRule *default_case = new RTLIL::CaseRule; + addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); + sw->cases.push_back(default_case); + } + + subst_lvalue_from = backup_subst_lvalue_from; + subst_lvalue_to = backup_subst_lvalue_to; + + subst_rvalue_from = backup_subst_rvalue_from; + subst_rvalue_to = backup_subst_rvalue_to; + + subst_rvalue_from.remove2(this_case_eq_lvalue, &subst_rvalue_to); + subst_rvalue_from.append(this_case_eq_lvalue); + subst_rvalue_from.optimize(); + subst_rvalue_to.append(this_case_eq_ltemp); + subst_rvalue_to.optimize(); + + this_case_eq_lvalue.replace(subst_lvalue_from, subst_lvalue_to); + removeSignalFromCaseTree(this_case_eq_lvalue, current_case); + addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp); + } + break; + + case AST_TCALL: + case AST_FOR: + break; + + default: + assert(0); + } + } +}; + +// create RTLIL from an AST node +// all generated cells, wires and processes are added to the module pointed to by 'current_module' +// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned. +// +// note that this function is influenced by a number of global variables that might be set when +// called from genWidthRTLIL(). also note that this function recursively calls itself to transform +// larger expressions into a netlist of cells. +RTLIL::SigSpec AstNode::genRTLIL(int width_hint) +{ + // in the following big switch() statement there are some uses of + // Clifford's Device (http://www.clifford.at/cfun/cliffdev/). In this + // cases this variable is used to hold the type of the cell that should + // be instanciated for this type of AST node. + std::string type_name; + + current_filename = filename; + set_line_num(linenum); + + switch (type) + { + // simply ignore this nodes. + // they are eighter leftovers from simplify() or are referenced by other nodes + // and are only accessed here thru this references + case AST_TASK: + case AST_FUNCTION: + case AST_AUTOWIRE: + case AST_PARAMETER: + case AST_LOCALPARAM: + case AST_GENVAR: + case AST_GENFOR: + case AST_GENIF: + break; + + // create an RTLIL::Wire for an AST_WIRE node + case AST_WIRE: { + if (current_module->wires.count(str) != 0) + log_error("Re-definition of signal `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + if (!range_valid) + log_error("Signal `%s' with non-constant width at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + + if (range_left < range_right && (range_left != -1 || range_right != 0)) { + int tmp = range_left; + range_left = range_right; + range_right = tmp; + } + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->name = str; + wire->width = range_left - range_right + 1; + wire->start_offset = range_right; + wire->port_id = port_id; + wire->port_input = is_input; + wire->port_output = is_output; + current_module->wires[wire->name] = wire; + + for (auto &attr : attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), filename.c_str(), linenum); + wire->attributes[attr.first].str = attr.second->str; + wire->attributes[attr.first].bits = attr.second->bits; + } + } + break; + + // create an RTLIL::Memory for an AST_MEMORY node + case AST_MEMORY: { + if (current_module->memories.count(str) != 0) + log_error("Re-definition of memory `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + + assert(children.size() >= 2); + assert(children[0]->type == AST_RANGE); + assert(children[1]->type == AST_RANGE); + + if (!children[0]->range_valid || !children[1]->range_valid) + log_error("Memory `%s' with non-constant width or size at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + + RTLIL::Memory *memory = new RTLIL::Memory; + memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + memory->name = str; + memory->width = children[0]->range_left - children[0]->range_right + 1; + memory->start_offset = children[0]->range_right; + memory->size = children[1]->range_left - children[1]->range_right; + current_module->memories[memory->name] = memory; + + if (memory->size < 0) + memory->size *= -1; + memory->size += std::min(children[1]->range_left, children[1]->range_right) + 1; + + for (auto &attr : attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), filename.c_str(), linenum); + memory->attributes[attr.first].str = attr.second->str; + memory->attributes[attr.first].bits = attr.second->bits; + } + } + break; + + // simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node + case AST_CONSTANT: + { + RTLIL::SigChunk chunk; + chunk.wire = NULL; + chunk.data.bits = bits; + chunk.width = bits.size(); + chunk.offset = 0; + + RTLIL::SigSpec sig; + sig.chunks.push_back(chunk); + sig.width = chunk.width; + return sig; + } + + // simply return the corresponding RTLIL::SigSpec for an AST_IDENTIFIER node + // for identifiers with dynamic bit ranges (e.g. "foo[bar]" or "foo[bar+3:bar]") a + // shifter cell is created and the output signal of this cell is returned + case AST_IDENTIFIER: + { + if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires.count(str) == 0) { + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->name = str; + if (width_hint >= 0) { + wire->width = width_hint; + log("Warning: Identifier `%s' is implicitly declared with width %d at %s:%d.\n", + str.c_str(), width_hint, filename.c_str(), linenum); + } else { + log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", + str.c_str(), filename.c_str(), linenum); + } + wire->auto_width = true; + current_module->wires[str] = wire; + } + else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE && + id2ast->type != AST_MEMORY) || current_module->wires.count(str) == 0) + log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + + if (id2ast->type == AST_MEMORY) + log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + + RTLIL::Wire *wire = current_module->wires[str]; + + RTLIL::SigChunk chunk; + chunk.wire = wire; + chunk.width = wire->width; + chunk.offset = 0; + + if (children.size() != 0) { + assert(children[0]->type == AST_RANGE); + if (!children[0]->range_valid) { + AstNode *left_at_zero_ast = children[0]->children[0]->clone(); + AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); + while (left_at_zero_ast->simplify(true, true, false, 1)) { } + while (right_at_zero_ast->simplify(true, true, false, 1)) { } + if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) + log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? + children[0]->children[1]->clone() : children[0]->children[0]->clone()); + fake_ast->children[0]->delete_children(); + RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shr", width, + fake_ast->children[0]->genRTLIL(), fake_ast->children[1]->genRTLIL()); + delete left_at_zero_ast; + delete right_at_zero_ast; + delete fake_ast; + return sig; + } else { + chunk.offset = children[0]->range_right - id2ast->range_right; + chunk.width = children[0]->range_left - children[0]->range_right + 1; + if (children[0]->range_left > id2ast->range_left || id2ast->range_right > children[0]->range_right) + log_error("Range select out of bounds on signal `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + } + } + + RTLIL::SigSpec sig; + sig.chunks.push_back(chunk); + sig.width = chunk.width; + + if (genRTLIL_subst_from && genRTLIL_subst_to) + sig.replace(*genRTLIL_subst_from, *genRTLIL_subst_to); + + is_signed = id2ast->is_signed; + if (children.size() != 0) + is_signed = false; + + return sig; + } + + // just pass thru the signal. the parent will evaluated the is_signed property and inperpret the SigSpec accordingly + case AST_TO_SIGNED: + case AST_TO_UNSIGNED: { + RTLIL::SigSpec sig = children[0]->genRTLIL(width_hint); + is_signed = type == AST_TO_SIGNED; + return sig; + } + + // concatenation of signals can be done directly using RTLIL::SigSpec + case AST_CONCAT: { + RTLIL::SigSpec sig; + sig.width = 0; + for (auto it = children.begin(); it != children.end(); it++) { + RTLIL::SigSpec s = (*it)->genRTLIL(); + for (size_t i = 0; i < s.chunks.size(); i++) { + sig.chunks.push_back(s.chunks[i]); + sig.width += s.chunks[i].width; + } + } + return sig; + } + + // replication of signals can be done directly using RTLIL::SigSpec + case AST_REPLICATE: { + RTLIL::SigSpec left = children[0]->genRTLIL(); + RTLIL::SigSpec right = children[1]->genRTLIL(); + if (!left.is_fully_const()) + log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); + int count = left.as_int(); + RTLIL::SigSpec sig; + for (int i = 0; i < count; i++) + sig.append(right); + is_signed = false; + return sig; + } + + // generate cells for unary operations: $not, $pos, $neg + if (0) { case AST_BIT_NOT: type_name = "$not"; } + if (0) { case AST_POS: type_name = "$pos"; } + if (0) { case AST_NEG: type_name = "$neg"; } + { + RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint); + is_signed = type == AST_NEG || (type == AST_POS && children[0]->is_signed); + int width = type == AST_NEG && arg.width < width_hint ? arg.width+1 : arg.width; + if (width > width_hint && width_hint > 0) + width = width_hint; + return uniop2rtlil(this, type_name, width, arg); + } + + // generate cells for binary operations: $and, $or, $xor, $xnor + if (0) { case AST_BIT_AND: type_name = "$and"; } + if (0) { case AST_BIT_OR: type_name = "$or"; } + if (0) { case AST_BIT_XOR: type_name = "$xor"; } + if (0) { case AST_BIT_XNOR: type_name = "$xnor"; } + { + RTLIL::SigSpec left = children[0]->genRTLIL(width_hint); + RTLIL::SigSpec right = children[1]->genRTLIL(width_hint); + int width = std::max(left.width, right.width); + if (width > width_hint && width_hint > 0) + width = width_hint; + return binop2rtlil(this, type_name, width, left, right); + } + + // generate cells for unary operations: $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor + if (0) { case AST_REDUCE_AND: type_name = "$reduce_and"; } + if (0) { case AST_REDUCE_OR: type_name = "$reduce_or"; } + if (0) { case AST_REDUCE_XOR: type_name = "$reduce_xor"; } + if (0) { case AST_REDUCE_XNOR: type_name = "$reduce_xnor"; } + { + RTLIL::SigSpec arg = children[0]->genRTLIL(); + RTLIL::SigSpec sig = uniop2rtlil(this, type_name, 1, arg); + return sig; + } + + // generate cells for unary operations: $reduce_bool + // (this is actually just an $reduce_or, but for clearity a different cell type is used) + if (0) { case AST_REDUCE_BOOL: type_name = "$reduce_bool"; } + { + RTLIL::SigSpec arg = children[0]->genRTLIL(); + RTLIL::SigSpec sig = arg.width > 1 ? uniop2rtlil(this, type_name, 1, arg) : arg; + return sig; + } + + // generate cells for binary operations: $shl, $shr, $sshl, $sshr + if (0) { case AST_SHIFT_LEFT: type_name = "$shl"; } + if (0) { case AST_SHIFT_RIGHT: type_name = "$shr"; } + if (0) { case AST_SHIFT_SLEFT: type_name = "$sshl"; is_signed = true; } + if (0) { case AST_SHIFT_SRIGHT: type_name = "$sshr"; is_signed = true; } + { + RTLIL::SigSpec left = children[0]->genRTLIL(width_hint); + RTLIL::SigSpec right = children[1]->genRTLIL(width_hint); + int width = width_hint > 0 ? width_hint : left.width; + return binop2rtlil(this, type_name, width, left, right); + } + + // generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt + if (0) { case AST_LT: type_name = "$lt"; } + if (0) { case AST_LE: type_name = "$le"; } + if (0) { case AST_EQ: type_name = "$eq"; } + if (0) { case AST_NE: type_name = "$ne"; } + if (0) { case AST_GE: type_name = "$ge"; } + if (0) { case AST_GT: type_name = "$gt"; } + { + RTLIL::SigSpec left = children[0]->genRTLIL(); + RTLIL::SigSpec right = children[1]->genRTLIL(); + RTLIL::SigSpec sig = binop2rtlil(this, type_name, 1, left, right); + return sig; + } + + // generate cells for binary operations: $add, $sub, $mul, $div, $mod, $pow + if (0) { case AST_ADD: type_name = "$add"; } + if (0) { case AST_SUB: type_name = "$sub"; } + if (0) { case AST_MUL: type_name = "$mul"; } + if (0) { case AST_DIV: type_name = "$div"; } + if (0) { case AST_MOD: type_name = "$mod"; } + if (0) { case AST_POW: type_name = "$pow"; } + { + RTLIL::SigSpec left = children[0]->genRTLIL(width_hint); + RTLIL::SigSpec right = children[1]->genRTLIL(width_hint); + int width = std::max(left.width, right.width); + if (width > width_hint && width_hint > 0) + width = width_hint; + if (width < width_hint) { + if (type == AST_ADD || type == AST_SUB) { + width++; + if (width < width_hint && children[0]->is_signed != children[1]->is_signed) + width++; + } + if (type == AST_SUB && !children[0]->is_signed && !children[1]->is_signed) + width = width_hint; + if (type == AST_MUL) + width = std::min(left.width + right.width, width_hint); + } + is_signed = children[0]->is_signed || children[1]->is_signed; + return binop2rtlil(this, type_name, width, left, right); + } + + // generate cells for binary operations: $logic_and, $logic_or + if (0) { case AST_LOGIC_AND: type_name = "$logic_and"; } + if (0) { case AST_LOGIC_OR: type_name = "$logic_or"; } + { + RTLIL::SigSpec left = children[0]->genRTLIL(); + RTLIL::SigSpec right = children[1]->genRTLIL(); + return binop2rtlil(this, type_name, 1, left, right); + } + + // generate cells for unary operations: $logic_not + case AST_LOGIC_NOT: + { + RTLIL::SigSpec arg = children[0]->genRTLIL(); + return uniop2rtlil(this, "$logic_not", 1, arg); + } + + // generate multiplexer for ternary operator (aka ?:-operator) + case AST_TERNARY: + { + RTLIL::SigSpec cond = children[0]->genRTLIL(); + RTLIL::SigSpec val1 = children[1]->genRTLIL(); + RTLIL::SigSpec val2 = children[2]->genRTLIL(); + + if (cond.width > 1) + cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false); + + int width = std::max(val1.width, val2.width); + if (children[1]->is_signed && children[2]->is_signed) { + is_signed = true; + val1.extend(width, children[1]->is_signed); + val2.extend(width, children[2]->is_signed); + } else { + is_signed = false; + val1.extend(width); + val2.extend(width); + } + + return mux2rtlil(this, cond, val1, val2); + } + + // generate $memrd cells for memory read ports + case AST_MEMRD: + { + std::stringstream sstr; + sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + + RTLIL::Cell *cell = new RTLIL::Cell; + cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + cell->name = sstr.str(); + cell->type = "$memrd"; + current_module->cells[cell->name] = cell; + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->name = cell->name + "_DATA"; + wire->width = current_module->memories[str]->width; + current_module->wires[wire->name] = wire; + + int addr_bits = 1; + while ((1 << addr_bits) < current_module->memories[str]->size) + addr_bits++; + + cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1); + cell->connections["\\ADDR"] = children[0]->genRTLIL(); + cell->connections["\\DATA"] = RTLIL::SigSpec(wire); + + cell->parameters["\\MEMID"] = RTLIL::Const(str); + cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); + cell->parameters["\\WIDTH"] = RTLIL::Const(wire->width); + + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + + return RTLIL::SigSpec(wire); + } + + // generate $memwr cells for memory write ports + case AST_MEMWR: + { + std::stringstream sstr; + sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + + RTLIL::Cell *cell = new RTLIL::Cell; + cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + cell->name = sstr.str(); + cell->type = "$memwr"; + current_module->cells[cell->name] = cell; + + int addr_bits = 1; + while ((1 << addr_bits) < current_module->memories[str]->size) + addr_bits++; + + cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1); + cell->connections["\\ADDR"] = children[0]->genRTLIL(); + cell->connections["\\DATA"] = children[1]->genRTLIL(); + cell->connections["\\EN"] = children[2]->genRTLIL(); + + cell->parameters["\\MEMID"] = RTLIL::Const(str); + cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); + cell->parameters["\\WIDTH"] = RTLIL::Const(current_module->memories[str]->width); + + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + } + break; + + // add entries to current_module->connections for assignments (outside of always blocks) + case AST_ASSIGN: + { + if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_AUTOWIRE) { + RTLIL::SigSpec right = children[1]->genRTLIL(); + RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.width); + current_module->connections.push_back(RTLIL::SigSig(left, right)); + } else { + RTLIL::SigSpec left = children[0]->genRTLIL(); + RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.width); + current_module->connections.push_back(RTLIL::SigSig(left, right)); + } + } + break; + + // create an RTLIL::Cell for an AST_CELL + case AST_CELL: + { + int port_counter = 0, para_counter = 0; + RTLIL::Cell *cell = new RTLIL::Cell; + cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + cell->name = str; + for (auto it = children.begin(); it != children.end(); it++) { + AstNode *child = *it; + if (child->type == AST_CELLTYPE) { + cell->type = child->str; + continue; + } + if (child->type == AST_PARASET) { + if (child->children[0]->type != AST_CONSTANT) + log_error("Parameter `%s' with non-constant value at %s:%d!\n", + child->str.c_str(), filename.c_str(), linenum); + if (child->str.size() == 0) { + char buf[100]; + snprintf(buf, 100, "$%d", ++para_counter); + cell->parameters[buf].str = child->children[0]->str; + cell->parameters[buf].bits = child->children[0]->bits; + } else { + cell->parameters[child->str].str = child->children[0]->str; + cell->parameters[child->str].bits = child->children[0]->bits; + } + continue; + } + if (child->type == AST_ARGUMENT) { + RTLIL::SigSpec sig; + if (child->children.size() > 0) + sig = child->children[0]->genRTLIL(); + if (child->str.size() == 0) { + char buf[100]; + snprintf(buf, 100, "$%d", ++port_counter); + cell->connections[buf] = sig; + } else { + cell->connections[child->str] = sig; + } + continue; + } + assert(0); + } + for (auto &attr : attributes) { + if (attr.second->type != AST_CONSTANT) + log_error("Attribute `%s' with non-constant value at %s:%d!\n", + attr.first.c_str(), filename.c_str(), linenum); + cell->attributes[attr.first].str = attr.second->str; + cell->attributes[attr.first].bits = attr.second->bits; + } + if (current_module->cells.count(cell->name) != 0) + log_error("Re-definition of cell `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + current_module->cells[str] = cell; + } + break; + + // use ProcessGenerator for always blocks + case AST_ALWAYS: { + AstNode *always = this->clone(); + ProcessGenerator generator(always); + delete always; + } break; + + // everything should have been handled above -> print error if not. + default: + for (auto f : log_files) + current_ast->dumpAst(f, "verilog-ast> "); + type_name = type2str(type); + log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n", + type_name.c_str(), filename.c_str(), linenum); + } + + return RTLIL::SigSpec(); +} + +// this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or +// signals must be substituted before beeing used as input values (used by ProcessGenerator) +// note that this is using some global variables to communicate this special settings to AstNode::genRTLIL(). +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, RTLIL::SigSpec *subst_from, RTLIL::SigSpec *subst_to) +{ + RTLIL::SigSpec *backup_subst_from = genRTLIL_subst_from; + RTLIL::SigSpec *backup_subst_to = genRTLIL_subst_to; + + if (subst_from) + genRTLIL_subst_from = subst_from; + if (subst_to) + genRTLIL_subst_to = subst_to; + + RTLIL::SigSpec sig = genRTLIL(width); + + genRTLIL_subst_from = backup_subst_from; + genRTLIL_subst_to = backup_subst_to; + + if (width >= 0) + sig.extend(width, is_signed); + + return sig; +} + diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc new file mode 100644 index 000000000..cb8b1043f --- /dev/null +++ b/frontends/ast/simplify.cc @@ -0,0 +1,1081 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This is the AST frontend library. + * + * The AST frontend library is not a frontend on it's own but provides a + * generic abstract syntax tree (AST) abstraction for HDL code and can be + * used by HDL frontends. See "ast.h" for an overview of the API and the + * Verilog frontend for an usage example. + * + */ + +#include "kernel/log.h" +#include "kernel/sha1.h" +#include "ast.h" + +#include +#include +#include + +using namespace AST; +using namespace AST_INTERNAL; + +// convert the AST into a simpler AST that has all parameters subsitited by their +// values, unrolled for-loops, expanded generate blocks, etc. when this function +// is done with an AST it can be converted into RTLIL using genRTLIL(). +// +// this function also does all name resolving and sets the id2ast member of all +// nodes that link to a different node using names and lexical scoping. +bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) +{ + AstNode *newNode = NULL; + bool did_something = false; + + if (stage == 0) + { + assert(type == AST_MODULE); + + while (simplify(const_fold, at_zero, in_lvalue, 1)) { } + + if (!flag_nomem2reg && attributes.count("\\nomem2reg") == 0) + { + std::set mem2reg_set, mem2reg_candidates; + mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, false, false); + + for (auto node : mem2reg_set) + { + int mem_width, mem_size, addr_bits; + node->meminfo(mem_width, mem_size, addr_bits); + + for (int i = 0; i < mem_size; i++) { + AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, + mkconst_int(mem_width-1, true), mkconst_int(0, true))); + reg->str = stringf("%s[%d]", node->str.c_str(), i); + reg->is_reg = true; + reg->is_signed = node->is_signed; + children.push_back(reg); + } + } + + mem2reg_as_needed_pass2(mem2reg_set, this, NULL); + + for (size_t i = 0; i < children.size(); i++) { + if (mem2reg_set.count(children[i]) > 0) { + delete children[i]; + children.erase(children.begin() + (i--)); + } + } + } + + while (simplify(const_fold, at_zero, in_lvalue, 2)) { } + return false; + } + + current_filename = filename; + set_line_num(linenum); + + // we do not look inside a task or function + // (but as soon as a task of function is instanciated we process the generated AST as usual) + if (type == AST_FUNCTION || type == AST_TASK) + return false; + + // deactivate all calls non-synthesis system taks + if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$stop" || str == "$finish")) { + delete_children(); + str = std::string(); + } + + // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) + if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_PARASET || type == AST_RANGE) + const_fold = true; + if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) + const_fold = true; + + std::map backup_scope; + + // create name resolution entries for all objects with names + // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;") + if (type == AST_MODULE) { + current_scope.clear(); + std::map this_wire_scope; + for (size_t i = 0; i < children.size(); i++) { + AstNode *node = children[i]; + if (node->type == AST_WIRE) { + if (this_wire_scope.count(node->str) > 0) { + AstNode *first_node = this_wire_scope[node->str]; + if (first_node->children.size() != node->children.size()) + goto wires_are_incompatible; + for (size_t j = 0; j < node->children.size(); j++) { + AstNode *n1 = first_node->children[j], *n2 = node->children[j]; + if (n1->type == AST_RANGE && n2->type == AST_RANGE && n1->range_valid && n2->range_valid) { + if (n1->range_left != n2->range_left) + goto wires_are_incompatible; + if (n1->range_right != n2->range_right) + goto wires_are_incompatible; + } else if (*n1 != *n2) + goto wires_are_incompatible; + } + if (first_node->range_left != node->range_left) + goto wires_are_incompatible; + if (first_node->range_right != node->range_right) + goto wires_are_incompatible; + if (first_node->port_id == 0 && (node->is_input || node->is_output)) + goto wires_are_incompatible; + if (node->is_input) + first_node->is_input = true; + if (node->is_output) + first_node->is_output = true; + if (node->is_reg) + first_node->is_reg = true; + if (node->is_signed) + first_node->is_signed = true; + for (auto &it : node->attributes) { + if (first_node->attributes.count(it.first) > 0) + delete first_node->attributes[it.first]; + first_node->attributes[it.first] = it.second->clone(); + } + children.erase(children.begin()+(i--)); + did_something = true; + delete node; + continue; + } + this_wire_scope[node->str] = node; + } + wires_are_incompatible: + if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || + node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) { + backup_scope[node->str] = current_scope[node->str]; + current_scope[node->str] = node; + } + } + } + + auto backup_current_block = current_block; + auto backup_current_block_child = current_block_child; + auto backup_current_top_block = current_top_block; + + // simplify all children first + // (iterate by index as e.g. auto wires can add new children in the process) + for (size_t i = 0; i < children.size(); i++) { + bool did_something_here = true; + if ((type == AST_GENFOR || type == AST_FOR) && i >= 3) + break; + if (type == AST_GENIF && i >= 1) + break; + while (did_something_here && i < children.size()) { + bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; + if (i == 0 && type == AST_REPLICATE) + const_fold_here = true; + if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) + in_lvalue_here = true; + if (type == AST_BLOCK) { + current_block = this; + current_block_child = children[i]; + } + if (type == AST_ALWAYS && children[i]->type == AST_BLOCK) + current_top_block = children[i]; + did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage); + if (did_something_here) + did_something = true; + } + } + for (auto &attr : attributes) { + while (attr.second->simplify(true, false, false, stage)) { } + } + + current_block = backup_current_block; + current_block_child = backup_current_block_child; + current_top_block = backup_current_top_block; + + for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { + if (it->second == NULL) + current_scope.erase(it->first); + else + current_scope[it->first] = it->second; + } + + current_filename = filename; + set_line_num(linenum); + + if (type == AST_MODULE) + current_scope.clear(); + + // annotate constant ranges + if (type == AST_RANGE) { + bool old_range_valid = range_valid; + range_valid = false; + range_left = -1; + range_right = 0; + assert(children.size() >= 1); + if (children[0]->type == AST_CONSTANT) { + range_valid = true; + range_left = children[0]->integer; + if (children.size() == 1) + range_right = range_left; + } + if (children.size() >= 2) { + if (children[1]->type == AST_CONSTANT) + range_right = children[1]->integer; + else + range_valid = false; + } + if (old_range_valid != range_valid) + did_something = true; + if (range_valid && range_left >= 0 && range_right > range_left) { + int tmp = range_right; + range_right = range_left; + range_left = tmp; + } + } + + // annotate wires with their ranges + if (type == AST_WIRE) { + if (children.size() > 0) { + if (children[0]->range_valid) { + if (!range_valid) + did_something = true; + range_valid = true; + range_left = children[0]->range_left; + range_right = children[0]->range_right; + } + } else { + if (!range_valid) + did_something = true; + range_valid = true; + range_left = 0; + range_right = 0; + } + } + + // annotate identifiers using scope resolution and create auto-wires as needed + if (type == AST_IDENTIFIER) { + if (current_scope.count(str) == 0) { + for (auto node : current_ast_mod->children) { + if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || + node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) && str == node->str) { + current_scope[node->str] = node; + break; + } + } + } + if (current_scope.count(str) == 0) { + log("Warning: Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str()); + AstNode *auto_wire = new AstNode(AST_AUTOWIRE); + auto_wire->str = str; + current_ast_mod->children.push_back(auto_wire); + current_scope[str] = auto_wire; + did_something = true; + } + id2ast = current_scope[str]; + } + + // unroll for loops and generate-for blocks + if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0) + { + AstNode *init_ast = children[0]; + AstNode *while_ast = children[1]; + AstNode *next_ast = children[2]; + AstNode *body_ast = children[3]; + + if (init_ast->type != AST_ASSIGN_EQ) + log_error("Unsupported 1st expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum); + if (next_ast->type != AST_ASSIGN_EQ) + log_error("Unsupported 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum); + + if (type == AST_GENFOR) { + if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR) + log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum); + if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR) + log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum); + } else { + if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE) + log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum); + if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE) + log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum); + } + + if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast) + log_error("Incompatible left-hand sides in 1st and 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum); + + // eval 1st expression + AstNode *varbuf = init_ast->children[1]->clone(); + while (varbuf->simplify(true, false, false, stage)) { } + + if (varbuf->type != AST_CONSTANT) + log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); + + varbuf = new AstNode(AST_LOCALPARAM, varbuf); + varbuf->str = init_ast->children[0]->str; + + AstNode *backup_scope_varbuf = current_scope[varbuf->str]; + current_scope[varbuf->str] = varbuf; + + size_t current_block_idx = 0; + if (type == AST_FOR) { + while (current_block_idx < current_block->children.size() && + current_block->children[current_block_idx] != current_block_child) + current_block_idx++; + } + + while (1) + { + // eval 2nd expression + AstNode *buf = while_ast->clone(); + while (buf->simplify(true, false, false, stage)) { } + + if (buf->type != AST_CONSTANT) + log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); + + if (buf->integer == 0) { + delete buf; + break; + } + delete buf; + + // expand body + int index = varbuf->children[0]->integer; + if (body_ast->type == AST_GENBLOCK) + buf = body_ast->clone(); + else + buf = new AstNode(AST_GENBLOCK, body_ast->clone()); + if (buf->str.empty()) { + std::stringstream sstr; + sstr << "$genblock$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + buf->str = sstr.str(); + } + std::map name_map; + std::stringstream sstr; + sstr << buf->str << "[" << index << "]."; + buf->expand_genblock(varbuf->str, sstr.str(), name_map); + + if (type == AST_GENFOR) { + for (size_t i = 0; i < buf->children.size(); i++) + current_ast_mod->children.push_back(buf->children[i]); + } else { + for (size_t i = 0; i < buf->children.size(); i++) + current_block->children.insert(current_block->children.begin() + current_block_idx++, buf->children[i]); + } + buf->children.clear(); + delete buf; + + // eval 3rd expression + buf = next_ast->children[1]->clone(); + while (buf->simplify(true, false, false, stage)) { } + + if (buf->type != AST_CONSTANT) + log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); + + delete varbuf->children[0]; + varbuf->children[0] = buf; + } + + current_scope[varbuf->str] = backup_scope_varbuf; + delete varbuf; + delete_children(); + did_something = true; + } + + // simplify generate-if blocks + if (type == AST_GENIF && children.size() != 0) + { + AstNode *buf = children[0]->clone(); + while (buf->simplify(true, false, false, stage)) { } + if (buf->type != AST_CONSTANT) { + for (auto f : log_files) + dumpAst(f, "verilog-ast> "); + log_error("Condition for generate if at %s:%d is not constant!\n", filename.c_str(), linenum); + } + if (buf->integer != 0) { + delete buf; + buf = children[1]->clone(); + } else { + delete buf; + buf = children.size() > 2 ? children[2]->clone() : NULL; + } + + if (buf) + { + if (buf->type != AST_GENBLOCK) + buf = new AstNode(AST_GENBLOCK, buf); + + if (!buf->str.empty()) { + std::map name_map; + buf->expand_genblock(std::string(), buf->str, name_map); + } + + for (size_t i = 0; i < buf->children.size(); i++) + current_ast_mod->children.push_back(buf->children[i]); + + buf->children.clear(); + delete buf; + } + + delete_children(); + did_something = true; + } + + // replace primitives with assignmens + if (type == AST_PRIMITIVE) + { + if (children.size() < 2) + log_error("Insufficient number of arguments for primitive `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + + std::vector children_list; + for (auto child : children) { + assert(child->type == AST_ARGUMENT); + assert(child->children.size() == 1); + children_list.push_back(child->children[0]); + child->children.clear(); + delete child; + } + children.clear(); + + AstNodeType op_type = AST_NONE; + bool invert_results = false; + + if (str == "and") + op_type = AST_BIT_AND; + if (str == "nand") + op_type = AST_BIT_AND, invert_results = true; + if (str == "or") + op_type = AST_BIT_OR; + if (str == "nor") + op_type = AST_BIT_OR, invert_results = true; + if (str == "xor") + op_type = AST_BIT_XOR; + if (str == "xnor") + op_type = AST_BIT_XOR, invert_results = true; + if (str == "buf") + op_type = AST_POS; + if (str == "not") + op_type = AST_POS, invert_results = true; + assert(op_type != AST_NONE); + + AstNode *node = children_list[1]; + if (op_type != AST_POS) + for (size_t i = 2; i < children_list.size(); i++) + node = new AstNode(op_type, node, children_list[i]); + if (invert_results) + node = new AstNode(AST_BIT_NOT, node); + + str.clear(); + type = AST_ASSIGN; + children.push_back(children_list[0]); + children.push_back(node); + did_something = true; + } + + // replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with + // a big case block that selects the correct single-bit assignment. + if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) { + if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0) + goto skip_dynamic_range_lvalue_expansion; + if (children[0]->children[0]->range_valid || did_something) + goto skip_dynamic_range_lvalue_expansion; + if (children[0]->id2ast == NULL || children[0]->id2ast->type != AST_WIRE) + goto skip_dynamic_range_lvalue_expansion; + if (!children[0]->id2ast->range_valid) + goto skip_dynamic_range_lvalue_expansion; + int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1; + int result_width = 1; + AstNode *shift_expr = NULL; + AstNode *range = children[0]->children[0]; + if (range->children.size() == 1) { + shift_expr = range->children[0]->clone(); + } else { + shift_expr = range->children[1]->clone(); + AstNode *left_at_zero_ast = range->children[0]->clone(); + AstNode *right_at_zero_ast = range->children[1]->clone(); + while (left_at_zero_ast->simplify(true, true, false, stage)) { } + while (right_at_zero_ast->simplify(true, true, false, stage)) { } + if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) + log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + result_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + } + did_something = true; + newNode = new AstNode(AST_CASE, shift_expr); + for (int i = 0; i <= source_width-result_width; i++) { + int start_bit = children[0]->id2ast->range_right + i; + AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true)); + AstNode *lvalue = children[0]->clone(); + lvalue->delete_children(); + lvalue->children.push_back(new AstNode(AST_RANGE, + mkconst_int(start_bit+result_width-1, true), mkconst_int(start_bit, true))); + cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone()))); + newNode->children.push_back(cond); + } + goto apply_newNode; + } +skip_dynamic_range_lvalue_expansion:; + + // found right-hand side identifier for memory -> replace with memory read port + if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue && + children[0]->type == AST_RANGE && children[0]->children.size() == 1) { + newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone()); + newNode->str = str; + goto apply_newNode; + } + + // assignment with memory in left-hand side expression -> replace with memory write port + if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER && + children[0]->children.size() == 1 && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && + children[0]->id2ast->children.size() >= 2 && children[0]->id2ast->children[0]->range_valid && + children[0]->id2ast->children[1]->range_valid) + { + std::stringstream sstr; + sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; + + if (type == AST_ASSIGN_EQ) + log("Warining: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", + filename.c_str(), linenum); + + int mem_width, mem_size, addr_bits; + children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); + + AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + wire_addr->str = id_addr; + current_ast_mod->children.push_back(wire_addr); + current_scope[wire_addr->str] = wire_addr; + + AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_data->str = id_data; + current_ast_mod->children.push_back(wire_data); + current_scope[wire_data->str] = wire_data; + + AstNode *wire_en = new AstNode(AST_WIRE); + wire_en->str = id_en; + current_ast_mod->children.push_back(wire_en); + current_scope[wire_en->str] = wire_en; + + std::vector x_bits; + x_bits.push_back(RTLIL::State::Sx); + + AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false)); + assign_addr->children[0]->str = id_addr; + + AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false)); + assign_data->children[0]->str = id_data; + + AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1)); + assign_en->children[0]->str = id_en; + + AstNode *default_signals = new AstNode(AST_BLOCK); + default_signals->children.push_back(assign_addr); + default_signals->children.push_back(assign_data); + default_signals->children.push_back(assign_en); + current_top_block->children.insert(current_top_block->children.begin(), default_signals); + + assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr->children[0]->str = id_addr; + + assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); + assign_data->children[0]->str = id_data; + + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); + assign_en->children[0]->str = id_en; + + newNode = new AstNode(AST_BLOCK); + newNode->children.push_back(assign_addr); + newNode->children.push_back(assign_data); + newNode->children.push_back(assign_en); + + AstNode *wrnode = new AstNode(AST_MEMWR); + wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + wrnode->str = children[0]->str; + wrnode->children[0]->str = id_addr; + wrnode->children[1]->str = id_data; + wrnode->children[2]->str = id_en; + current_ast_mod->children.push_back(wrnode); + + goto apply_newNode; + } + + // replace function and task calls with the code from the function or task + if ((type == AST_FCALL || type == AST_TCALL) && !str.empty()) + { + if (type == AST_FCALL) { + if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION) + log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + } + if (type == AST_TCALL) { + if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK) + log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + } + + AstNode *decl = current_scope[str]; + std::stringstream sstr; + sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++) << "$"; + std::string prefix = sstr.str(); + + size_t arg_count = 0; + std::map replace_rules; + + if (current_block == NULL) + { + assert(type == AST_FCALL); + + AstNode *wire = NULL; + for (auto child : decl->children) + if (child->type == AST_WIRE && child->str == str) + wire = child->clone(); + assert(wire != NULL); + + wire->str = prefix + str; + wire->port_id = 0; + wire->is_input = false; + wire->is_output = false; + + current_ast_mod->children.push_back(wire); + + AstNode *lvalue = new AstNode(AST_IDENTIFIER); + lvalue->str = wire->str; + + AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, + new AstNode(AST_ASSIGN_EQ, lvalue, clone()))); + current_ast_mod->children.push_back(always); + + goto replace_fcall_with_id; + } + + for (auto child : decl->children) + { + if (child->type == AST_WIRE) + { + AstNode *wire = child->clone(); + wire->str = prefix + wire->str; + wire->port_id = 0; + wire->is_input = false; + wire->is_output = false; + current_ast_mod->children.push_back(wire); + + replace_rules[child->str] = wire->str; + + if (child->is_input && arg_count < children.size()) + { + AstNode *arg = children[arg_count++]->clone(); + AstNode *wire_id = new AstNode(AST_IDENTIFIER); + wire_id->str = wire->str; + AstNode *assign = new AstNode(AST_ASSIGN_EQ, wire_id, arg); + + for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { + if (*it != current_block_child) + continue; + current_block->children.insert(it, assign); + break; + } + } + } + else + { + AstNode *stmt = child->clone(); + stmt->replace_ids(replace_rules); + + for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { + if (*it != current_block_child) + continue; + current_block->children.insert(it, stmt); + break; + } + } + } + + replace_fcall_with_id: + if (type == AST_FCALL) { + delete_children(); + type = AST_IDENTIFIER; + str = prefix + str; + } + if (type == AST_TCALL) + str = ""; + did_something = true; + } + + // perform const folding when activated + if (const_fold && newNode == NULL) + { + RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, int); + RTLIL::Const dummy_arg; + + switch (type) + { + case AST_IDENTIFIER: + if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) { + if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) { + if (current_scope[str]->children[0]->type == AST_CONSTANT) { + std::vector data; + for (int i = children[0]->range_right; i <= children[0]->range_left; i++) + data.push_back(current_scope[str]->children[0]->bits[i]); + newNode = mkconst_bits(data, false); + } + } else + if (children.size() == 0) + newNode = current_scope[str]->children[0]->clone(); + } + else if (at_zero && current_module->wires.count(str) > 0) { + assert(current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)); + if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) + newNode = mkconst_int(0, false, children[0]->range_left - children[0]->range_right + 1); + else + if (children.size() == 0) + newNode = mkconst_int(0, current_scope[str]->is_signed, current_module->wires[str]->width); + } + break; + case AST_BIT_NOT: + if (children[0]->type == AST_CONSTANT) { + RTLIL::Const y = RTLIL::const_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); + newNode = mkconst_bits(y.bits, false); + } + break; + if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; } + if (0) { case AST_BIT_OR: const_func = RTLIL::const_or; } + if (0) { case AST_BIT_XOR: const_func = RTLIL::const_xor; } + if (0) { case AST_BIT_XNOR: const_func = RTLIL::const_xnor; } + if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), + children[0]->is_signed, children[1]->is_signed, -1); + newNode = mkconst_bits(y.bits, false); + } + break; + if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; } + if (0) { case AST_REDUCE_OR: const_func = RTLIL::const_reduce_or; } + if (0) { case AST_REDUCE_XOR: const_func = RTLIL::const_reduce_xor; } + if (0) { case AST_REDUCE_XNOR: const_func = RTLIL::const_reduce_xnor; } + if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; } + if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); + newNode = mkconst_bits(y.bits, false); + } + break; + case AST_LOGIC_NOT: + if (children[0]->type == AST_CONSTANT) { + RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); + newNode = mkconst_bits(y.bits, false); + } + break; + if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } + if (0) { case AST_LOGIC_OR: const_func = RTLIL::const_logic_or; } + if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), + children[0]->is_signed, children[1]->is_signed, -1); + newNode = mkconst_bits(y.bits, false); + } + break; + if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; } + if (0) { case AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; } + if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; } + if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; } + if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), children[0]->is_signed, false, -1); + newNode = mkconst_bits(y.bits, children[0]->is_signed); + } + break; + if (0) { case AST_LT: const_func = RTLIL::const_lt; } + if (0) { case AST_LE: const_func = RTLIL::const_le; } + if (0) { case AST_EQ: const_func = RTLIL::const_eq; } + if (0) { case AST_NE: const_func = RTLIL::const_ne; } + if (0) { case AST_GE: const_func = RTLIL::const_ge; } + if (0) { case AST_GT: const_func = RTLIL::const_gt; } + if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), + children[0]->is_signed, children[1]->is_signed, -1); + newNode = mkconst_bits(y.bits, false); + } + break; + if (0) { case AST_ADD: const_func = RTLIL::const_add; } + if (0) { case AST_SUB: const_func = RTLIL::const_sub; } + if (0) { case AST_MUL: const_func = RTLIL::const_mul; } + if (0) { case AST_DIV: const_func = RTLIL::const_div; } + if (0) { case AST_MOD: const_func = RTLIL::const_mod; } + if (0) { case AST_POW: const_func = RTLIL::const_pow; } + if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), + children[0]->is_signed, children[1]->is_signed, -1); + newNode = mkconst_bits(y.bits, children[0]->is_signed && children[1]->is_signed); + } + break; + if (0) { case AST_POS: const_func = RTLIL::const_pos; } + if (0) { case AST_NEG: const_func = RTLIL::const_neg; } + if (children[0]->type == AST_CONSTANT) { + RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); + newNode = mkconst_bits(y.bits, children[0]->is_signed); + } + break; + case AST_TERNARY: + if (children[0]->type == AST_CONSTANT) { + if (children[0]->integer) + newNode = children[1]->clone(); + else + newNode = children[2]->clone(); + } + break; + default: + break; + } + } + + // if any of the above set 'newNode' -> use 'newNode' as template to update 'this' + if (newNode) { +apply_newNode: + // fprintf(stderr, "----\n"); + // dumpAst(stderr, "- "); + // newNode->dumpAst(stderr, "+ "); + assert(newNode != NULL); + newNode->filename = filename; + newNode->linenum = linenum; + newNode->cloneInto(this); + delete newNode; + did_something = true; + } + + return did_something; +} + +// annotate the names of all wires and other named objects in a generate block +void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map &name_map) +{ + if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { + current_scope[index_var]->children[0]->cloneInto(this); + return; + } + + if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0) { + str = name_map[str]; + return; + } + + std::map backup_name_map; + + for (size_t i = 0; i < children.size(); i++) { + AstNode *child = children[i]; + if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || + child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) { + if (backup_name_map.size() == 0) + backup_name_map = name_map; + std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; + size_t pos = child->str.rfind('.'); + if (pos == std::string::npos) + pos = child->str[0] == '\\' ? 1 : 0; + else + pos = pos + 1; + new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); + if (new_name[0] != '$' && new_name[0] != '\\') + new_name = prefix[0] + new_name; + name_map[child->str] = new_name; + child->str = new_name; + } + } + + for (size_t i = 0; i < children.size(); i++) { + AstNode *child = children[i]; + if (child->type != AST_FUNCTION && child->type != AST_TASK) + child->expand_genblock(index_var, prefix, name_map); + } + + if (backup_name_map.size() > 0) + name_map.swap(backup_name_map); +} + +// rename stuff (used when tasks of functions are instanciated) +void AstNode::replace_ids(std::map &rules) +{ + if (type == AST_IDENTIFIER && rules.count(str) > 0) + str = rules[str]; + for (auto child : children) + child->replace_ids(rules); +} + +// find memories that should be replaced by registers +void AstNode::mem2reg_as_needed_pass1(std::set &mem2reg_set, std::set &mem2reg_candidates, bool sync_proc, bool async_proc) +{ + if ((type == AST_ASSIGN_LE && async_proc) || (type == AST_ASSIGN_EQ && (sync_proc || async_proc))) + if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && + children[0]->id2ast->attributes.count("\\nomem2reg") == 0) { + if (async_proc || mem2reg_candidates.count(children[0]->id2ast) > 0) { + if (mem2reg_set.count(children[0]->id2ast) == 0) + log("Warning: Replacing memory %s with list of registers because of assignment in line %s:%d.\n", + children[0]->str.c_str(), filename.c_str(), linenum); + mem2reg_set.insert(children[0]->id2ast); + } + mem2reg_candidates.insert(children[0]->id2ast); + } + + if (type == AST_ALWAYS) { + for (auto child : children) { + if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) + sync_proc = true; + } + async_proc = !sync_proc; + } + + for (auto child : children) + child->mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, sync_proc, async_proc); +} + +// actually replace memories with registers +void AstNode::mem2reg_as_needed_pass2(std::set &mem2reg_set, AstNode *mod, AstNode *block) +{ + if (type == AST_BLOCK) + block = this; + + if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && + children[0]->id2ast && mem2reg_set.count(children[0]->id2ast) > 0) + { + std::stringstream sstr; + sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; + + int mem_width, mem_size, addr_bits; + children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); + + AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + wire_addr->str = id_addr; + wire_addr->is_reg = true; + mod->children.push_back(wire_addr); + + AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_data->str = id_data; + wire_data->is_reg = true; + mod->children.push_back(wire_data); + + assert(block != NULL); + size_t assign_idx = 0; + while (assign_idx < block->children.size() && block->children[assign_idx] != this) + assign_idx++; + assert(assign_idx < block->children.size()); + + AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr->children[0]->str = id_addr; + block->children.insert(block->children.begin()+assign_idx+1, assign_addr); + + AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); + case_node->children[0]->str = id_addr; + for (int i = 0; i < mem_size; i++) { + if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i) + continue; + AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); + AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i); + assign_reg->children[1]->str = id_data; + cond_node->children[1]->children.push_back(assign_reg); + case_node->children.push_back(cond_node); + } + block->children.insert(block->children.begin()+assign_idx+2, case_node); + + children[0]->delete_children(); + children[0]->range_valid = false; + children[0]->id2ast = NULL; + children[0]->str = id_data; + } + + if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0) + { + std::stringstream sstr; + sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; + + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); + + AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + wire_addr->str = id_addr; + mod->children.push_back(wire_addr); + + AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_data->str = id_data; + mod->children.push_back(wire_data); + + AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); + assign_addr->children[0]->str = id_addr; + + AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); + case_node->children[0]->str = id_addr; + + for (int i = 0; i < mem_size; i++) { + if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i) + continue; + AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); + AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + assign_reg->children[0]->str = id_data; + assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i); + cond_node->children[1]->children.push_back(assign_reg); + case_node->children.push_back(cond_node); + } + + std::vector x_bits; + x_bits.push_back(RTLIL::State::Sx); + AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK)); + AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false)); + assign_reg->children[0]->str = id_data; + cond_node->children[1]->children.push_back(assign_reg); + case_node->children.push_back(cond_node); + + if (block) + { + size_t assign_idx = 0; + while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) + assign_idx++; + assert(assign_idx < block->children.size()); + block->children.insert(block->children.begin()+assign_idx, case_node); + block->children.insert(block->children.begin()+assign_idx, assign_addr); + wire_addr->is_reg = true; + wire_data->is_reg = true; + } + else + { + AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); + proc->children[0]->children.push_back(case_node); + mod->children.push_back(proc); + mod->children.push_back(assign_addr); + } + + delete_children(); + range_valid = false; + id2ast = NULL; + str = id_data; + } + + assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); + + for (size_t i = 0; i < children.size(); i++) + children[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block); +} + +// calulate memory dimensions +void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) +{ + assert(type == AST_MEMORY); + + mem_width = children[0]->range_left - children[0]->range_right + 1; + mem_size = children[1]->range_left - children[1]->range_right; + + if (mem_size < 0) + mem_size *= -1; + mem_size += std::min(children[1]->range_left, children[1]->range_right) + 1; + + addr_bits = 1; + while ((1 << addr_bits) < mem_size) + addr_bits++; +} + diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc new file mode 100644 index 000000000..07ebf0856 --- /dev/null +++ b/frontends/ilang/Makefile.inc @@ -0,0 +1,16 @@ + +GENFILES += frontends/ilang/parser.tab.cc +GENFILES += frontends/ilang/parser.tab.h +GENFILES += frontends/ilang/parser.output +GENFILES += frontends/ilang/lexer.cc + +frontends/ilang/parser.tab.cc frontends/ilang/parser.tab.h: frontends/ilang/parser.y + bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y + mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc + +frontends/ilang/lexer.cc: frontends/ilang/lexer.l + flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l + +OBJS += frontends/ilang/parser.tab.o frontends/ilang/lexer.o +OBJS += frontends/ilang/ilang_frontend.o + diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc new file mode 100644 index 000000000..f3ad3a199 --- /dev/null +++ b/frontends/ilang/ilang_frontend.cc @@ -0,0 +1,49 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A very simple and straightforward frontend for the RTLIL text + * representation (as generated by the 'ilang' backend). + * + */ + +#include "ilang_frontend.h" +#include "kernel/register.h" +#include "kernel/log.h" + +void rtlil_frontend_ilang_yyerror(char const *s) +{ + log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s); +} + +struct IlangFrontend : public Frontend { + IlangFrontend() : Frontend("ilang") { } + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + log_header("Executing ILANG frontend.\n"); + extra_args(f, filename, args, 1); + log("Input filename: %s\n", filename.c_str()); + + ILANG_FRONTEND::current_design = design; + rtlil_frontend_ilang_yydebug = false; + rtlil_frontend_ilang_yyrestart(f); + rtlil_frontend_ilang_yyparse(); + rtlil_frontend_ilang_yylex_destroy(); + } +} IlangFrontend; + diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h new file mode 100644 index 000000000..5e768c3b9 --- /dev/null +++ b/frontends/ilang/ilang_frontend.h @@ -0,0 +1,45 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A very simple and straightforward frontend for the RTLIL text + * representation (as generated by the 'ilang' backend). + * + */ + +#ifndef ILANG_FRONTEND_H +#define ILANG_FRONTEND_H + +#include "kernel/rtlil.h" +#include + +namespace ILANG_FRONTEND { + void ilang_frontend(FILE *f, RTLIL::Design *design); + extern RTLIL::Design *current_design; +} + +extern int rtlil_frontend_ilang_yydebug; +int rtlil_frontend_ilang_yylex(void); +void rtlil_frontend_ilang_yyerror(char const *s); +void rtlil_frontend_ilang_yyrestart(FILE *f); +int rtlil_frontend_ilang_yyparse(void); +void rtlil_frontend_ilang_yylex_destroy(void); +int rtlil_frontend_ilang_yyget_lineno(void); + +#endif + diff --git a/frontends/ilang/lexer.l b/frontends/ilang/lexer.l new file mode 100644 index 000000000..e331c267e --- /dev/null +++ b/frontends/ilang/lexer.l @@ -0,0 +1,122 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A very simple and straightforward frontend for the RTLIL text + * representation (as generated by the 'ilang' backend). + * + */ + +%{ +#include "kernel/rtlil.h" +#include "parser.tab.h" +%} + +%option yylineno +%option noyywrap +%option nounput +%option prefix="rtlil_frontend_ilang_yy" + +%x STRING + +%% + +"module" { return TOK_MODULE; } +"attribute" { return TOK_ATTRIBUTE; } +"parameter" { return TOK_PARAMETER; } +"wire" { return TOK_WIRE; } +"memory" { return TOK_MEMORY; } +"auto" { return TOK_AUTO; } +"width" { return TOK_WIDTH; } +"offset" { return TOK_OFFSET; } +"size" { return TOK_SIZE; } +"input" { return TOK_INPUT; } +"output" { return TOK_OUTPUT; } +"inout" { return TOK_INOUT; } +"cell" { return TOK_CELL; } +"connect" { return TOK_CONNECT; } +"switch" { return TOK_SWITCH; } +"case" { return TOK_CASE; } +"assign" { return TOK_ASSIGN; } +"sync" { return TOK_SYNC; } +"low" { return TOK_LOW; } +"high" { return TOK_HIGH; } +"posedge" { return TOK_POSEDGE; } +"negedge" { return TOK_NEGEDGE; } +"edge" { return TOK_EDGE; } +"always" { return TOK_ALWAYS; } +"update" { return TOK_UPDATE; } +"process" { return TOK_PROCESS; } +"end" { return TOK_END; } + +[a-z]+ { return TOK_INVALID; } + +"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } +"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } +"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } + +[0-9]+'[01xzm-]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; } +[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; } + +\" { BEGIN(STRING); } +\\. { yymore(); } +\" { + BEGIN(0); + char *yystr = strdup(yytext); + yystr[strlen(yytext) - 1] = 0; + int i = 0, j = 0; + while (yystr[i]) { + if (yystr[i] == '\\' && yystr[i + 1]) { + i++; + if (yystr[i] == 'n') + yystr[i] = '\n'; + else if (yystr[i] == 't') + yystr[i] = '\t'; + else if ('0' <= yystr[i] && yystr[i] <= '7') { + yystr[i] = yystr[i] - '0'; + if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { + yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; + i++; + } + if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { + yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; + i++; + } + } + } + yystr[j++] = yystr[i++]; + } + yystr[j] = 0; + rtlil_frontend_ilang_yylval.string = yystr; + return TOK_STRING; +} +. { yymore(); } + +"#"[^\n]*\n /* ignore comments */ +[ \t] /* ignore non-newline whitespaces */ +[\r\n]+ { return TOK_EOL; } + +. { return *yytext; } + +%% + +// this is a hack to avoid the 'yyinput defined but not used' error msgs +void *rtlil_frontend_ilang_avoid_input_warnings() { + return (void*)&yyinput; +} + diff --git a/frontends/ilang/parser.y b/frontends/ilang/parser.y new file mode 100644 index 000000000..61bac830f --- /dev/null +++ b/frontends/ilang/parser.y @@ -0,0 +1,416 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A very simple and straightforward frontend for the RTLIL text + * representation (as generated by the 'ilang' backend). + * + */ + +%{ +#include +#include "ilang_frontend.h" +namespace ILANG_FRONTEND { + RTLIL::Design *current_design; + RTLIL::Module *current_module; + RTLIL::Wire *current_wire; + RTLIL::Memory *current_memory; + RTLIL::Cell *current_cell; + RTLIL::Process *current_process; + std::vector*> switch_stack; + std::vector case_stack; + std::map attrbuf; +} +using namespace ILANG_FRONTEND; +%} + +%name-prefix="rtlil_frontend_ilang_yy" + +%union { + char *string; + int integer; + RTLIL::Const *data; + RTLIL::SigSpec *sigspec; +} + +%token TOK_ID TOK_VALUE TOK_STRING +%token TOK_INT +%token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT +%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC +%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS +%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET +%token TOK_PARAMETER TOK_ATTRIBUTE TOK_AUTO TOK_MEMORY TOK_SIZE + +%type sigspec sigspec_list +%type sync_type +%type constant + +%expect 0 +%debug + +%% + +input: + optional_eol { + attrbuf.clear(); + } design { + if (attrbuf.size() != 0) + rtlil_frontend_ilang_yyerror("dangling attribute"); + }; + +optional_eol: + optional_eol TOK_EOL | /* empty */; + +design: + design module | + design attr_stmt | + /* empty */; + +module: + TOK_MODULE TOK_ID TOK_EOL { + if (current_design->modules.count($2) != 0) + rtlil_frontend_ilang_yyerror("scope error"); + current_module = new RTLIL::Module; + current_module->name = $2; + current_module->attributes = attrbuf; + current_design->modules[$2] = current_module; + attrbuf.clear(); + free($2); + } module_body TOK_END { + if (attrbuf.size() != 0) + rtlil_frontend_ilang_yyerror("dangling attribute"); + } TOK_EOL; + +module_body: + module_body module_stmt | + /* empty */; + +module_stmt: + attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt; + +attr_stmt: + TOK_ATTRIBUTE TOK_ID constant TOK_EOL { + attrbuf[$2] = *$3; + delete $3; + }; + +wire_stmt: + TOK_WIRE { + current_wire = new RTLIL::Wire; + current_wire->attributes = attrbuf; + attrbuf.clear(); + } wire_options TOK_ID TOK_EOL { + if (current_module->wires.count($4) != 0) + rtlil_frontend_ilang_yyerror("scope error"); + current_wire->name = $4; + current_module->wires[$4] = current_wire; + free($4); + }; + +wire_options: + wire_options TOK_AUTO { + current_wire->auto_width = true; + } | + wire_options TOK_WIDTH TOK_INT { + current_wire->width = $3; + } | + wire_options TOK_OFFSET TOK_INT { + current_wire->start_offset = $3; + } | + wire_options TOK_INPUT TOK_INT { + current_wire->port_id = $3; + current_wire->port_input = true; + current_wire->port_output = false; + } | + wire_options TOK_OUTPUT TOK_INT { + current_wire->port_id = $3; + current_wire->port_input = false; + current_wire->port_output = true; + } | + wire_options TOK_INOUT TOK_INT { + current_wire->port_id = $3; + current_wire->port_input = true; + current_wire->port_output = true; + } | + /* empty */; + +memory_stmt: + TOK_MEMORY { + current_memory = new RTLIL::Memory; + current_memory->attributes = attrbuf; + attrbuf.clear(); + } memory_options TOK_ID TOK_EOL { + if (current_module->memories.count($4) != 0) + rtlil_frontend_ilang_yyerror("scope error"); + current_memory->name = $4; + current_module->memories[$4] = current_memory; + free($4); + }; + +memory_options: + memory_options TOK_WIDTH TOK_INT { + current_wire->width = $3; + } | + memory_options TOK_SIZE TOK_INT { + current_memory->size = $3; + } | + /* empty */; + +cell_stmt: + TOK_CELL TOK_ID TOK_ID TOK_EOL { + if (current_module->cells.count($3) != 0) + rtlil_frontend_ilang_yyerror("scope error"); + current_cell = new RTLIL::Cell; + current_cell->type = $2; + current_cell->name = $3; + current_cell->attributes = attrbuf; + current_module->cells[$3] = current_cell; + attrbuf.clear(); + free($2); + free($3); + } cell_body TOK_END TOK_EOL; + +cell_body: + cell_body TOK_PARAMETER TOK_ID constant TOK_EOL { + current_cell->parameters[$3] = *$4; + free($3); + delete $4; + } | + cell_body TOK_CONNECT TOK_ID sigspec TOK_EOL { + if (current_cell->connections.count($3) != 0) + rtlil_frontend_ilang_yyerror("scope error"); + current_cell->connections[$3] = *$4; + delete $4; + free($3); + } | + /* empty */; + +proc_stmt: + TOK_PROCESS TOK_ID TOK_EOL { + if (current_module->processes.count($2) != 0) + rtlil_frontend_ilang_yyerror("scope error"); + current_process = new RTLIL::Process; + current_process->name = $2; + current_process->attributes = attrbuf; + current_module->processes[$2] = current_process; + switch_stack.clear(); + switch_stack.push_back(¤t_process->root_case.switches); + case_stack.clear(); + case_stack.push_back(¤t_process->root_case); + free($2); + } case_body sync_list TOK_END TOK_EOL; + +switch_stmt: + attr_list TOK_SWITCH sigspec TOK_EOL { + RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; + rule->signal = *$3; + rule->attributes = attrbuf; + switch_stack.back()->push_back(rule); + attrbuf.clear(); + delete $3; + } switch_body TOK_END TOK_EOL; + +attr_list: + /* empty */ | + attr_list attr_stmt; + +switch_body: + switch_body TOK_CASE { + RTLIL::CaseRule *rule = new RTLIL::CaseRule; + switch_stack.back()->back()->cases.push_back(rule); + switch_stack.push_back(&rule->switches); + case_stack.push_back(rule); + } compare_list TOK_EOL case_body { + switch_stack.pop_back(); + case_stack.pop_back(); + } | + /* empty */; + +compare_list: + sigspec { + case_stack.back()->compare.push_back(*$1); + delete $1; + } | + compare_list ',' sigspec { + case_stack.back()->compare.push_back(*$3); + delete $3; + } | + /* empty */; + +case_body: + switch_stmt case_body | + assign_stmt case_body | + /* empty */; + +assign_stmt: + TOK_ASSIGN sigspec sigspec TOK_EOL { + case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); + delete $2; + delete $3; + }; + +sync_list: + sync_list TOK_SYNC sync_type sigspec TOK_EOL { + RTLIL::SyncRule *rule = new RTLIL::SyncRule; + rule->type = RTLIL::SyncType($3); + rule->signal = *$4; + current_process->syncs.push_back(rule); + delete $4; + } update_list | + sync_list TOK_SYNC TOK_ALWAYS TOK_EOL { + RTLIL::SyncRule *rule = new RTLIL::SyncRule; + rule->type = RTLIL::SyncType::STa; + rule->signal = RTLIL::SigSpec(); + current_process->syncs.push_back(rule); + } update_list | + /* empty */; + +sync_type: + TOK_LOW { $$ = RTLIL::ST0; } | + TOK_HIGH { $$ = RTLIL::ST1; } | + TOK_POSEDGE { $$ = RTLIL::STp; } | + TOK_NEGEDGE { $$ = RTLIL::STn; } | + TOK_EDGE { $$ = RTLIL::STe; }; + +update_list: + update_list TOK_UPDATE sigspec sigspec TOK_EOL { + current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4)); + delete $3; + delete $4; + } | + /* empty */; + +constant: + TOK_VALUE { + char *ep; + int width = strtol($1, &ep, 10); + std::list bits; + while (*(++ep) != 0) { + RTLIL::State bit = RTLIL::Sx; + switch (*ep) { + case '0': bit = RTLIL::S0; break; + case '1': bit = RTLIL::S1; break; + case 'x': bit = RTLIL::Sx; break; + case 'z': bit = RTLIL::Sz; break; + case '-': bit = RTLIL::Sa; break; + case 'm': bit = RTLIL::Sm; break; + } + bits.push_front(bit); + } + if (bits.size() == 0) + bits.push_back(RTLIL::Sx); + while ((int)bits.size() < width) { + RTLIL::State bit = bits.back(); + if (bit == RTLIL::S1) + bit = RTLIL::S0; + bits.push_back(bit); + } + while ((int)bits.size() > width) + bits.pop_back(); + $$ = new RTLIL::Const; + for (auto it = bits.begin(); it != bits.end(); it++) + $$->bits.push_back(*it); + free($1); + } | + TOK_INT { + $$ = new RTLIL::Const($1, 32); + } | + TOK_STRING { + $$ = new RTLIL::Const($1); + free($1); + }; + +sigspec: + constant { + RTLIL::SigChunk chunk; + chunk.wire = NULL; + chunk.width = $1->bits.size(); + chunk.offset = 0; + chunk.data = *$1; + $$ = new RTLIL::SigSpec; + $$->chunks.push_back(chunk); + $$->width = chunk.width; + delete $1; + } | + TOK_ID { + if (current_module->wires.count($1) == 0) + rtlil_frontend_ilang_yyerror("scope error"); + RTLIL::SigChunk chunk; + chunk.wire = current_module->wires[$1]; + chunk.width = current_module->wires[$1]->width; + chunk.offset = 0; + $$ = new RTLIL::SigSpec; + $$->chunks.push_back(chunk); + $$->width = chunk.width; + free($1); + } | + TOK_ID '[' TOK_INT ']' { + if (current_module->wires.count($1) == 0) + rtlil_frontend_ilang_yyerror("scope error"); + RTLIL::SigChunk chunk; + chunk.wire = current_module->wires[$1]; + chunk.offset = $3; + chunk.width = 1; + $$ = new RTLIL::SigSpec; + $$->chunks.push_back(chunk); + $$->width = 1; + free($1); + } | + TOK_ID '[' TOK_INT ':' TOK_INT ']' { + if (current_module->wires.count($1) == 0) + rtlil_frontend_ilang_yyerror("scope error"); + RTLIL::SigChunk chunk; + chunk.wire = current_module->wires[$1]; + chunk.width = $3 - $5 + 1; + chunk.offset = $5; + $$ = new RTLIL::SigSpec; + $$->chunks.push_back(chunk); + $$->width = chunk.width; + free($1); + } | + '{' sigspec_list '}' { + $$ = $2; + }; + +sigspec_list: + sigspec_list sigspec { + $$ = new RTLIL::SigSpec; + for (auto it = $2->chunks.begin(); it != $2->chunks.end(); it++) { + $$->chunks.push_back(*it); + $$->width += it->width; + } + for (auto it = $1->chunks.begin(); it != $1->chunks.end(); it++) { + $$->chunks.push_back(*it); + $$->width += it->width; + } + delete $1; + delete $2; + } | + /* empty */ { + $$ = new RTLIL::SigSpec; + }; + +conn_stmt: + TOK_CONNECT sigspec sigspec TOK_EOL { + if (attrbuf.size() != 0) + rtlil_frontend_ilang_yyerror("dangling attribute"); + current_module->connections.push_back(RTLIL::SigSig(*$2, *$3)); + delete $2; + delete $3; + }; + diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc new file mode 100644 index 000000000..6693f2d1b --- /dev/null +++ b/frontends/verilog/Makefile.inc @@ -0,0 +1,19 @@ + +GENFILES += frontends/verilog/parser.tab.cc +GENFILES += frontends/verilog/parser.tab.h +GENFILES += frontends/verilog/parser.output +GENFILES += frontends/verilog/lexer.cc + +frontends/verilog/parser.tab.cc frontends/verilog/parser.tab.h: frontends/verilog/parser.y + bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y + mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc + +frontends/verilog/lexer.cc: frontends/verilog/lexer.l + flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l + +OBJS += frontends/verilog/parser.tab.o +OBJS += frontends/verilog/lexer.o +OBJS += frontends/verilog/preproc.o +OBJS += frontends/verilog/verilog_frontend.o +OBJS += frontends/verilog/const2ast.o + diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc new file mode 100644 index 000000000..e5beaeade --- /dev/null +++ b/frontends/verilog/const2ast.cc @@ -0,0 +1,197 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * This file contains an ad-hoc parser for Verilog constants. The Verilog + * lexer does only recognize a constant but does not actually split it to its + * components. I.e. it just passes the Verilog code for the constant to the + * bison parser. The parser then uses the function const2ast() from this file + * to create an AST node for the constant. + * + */ + +#include "verilog_frontend.h" +#include "kernel/log.h" +#include +#include +#include + +using namespace AST; + +// divide an arbitrary length decimal number by two and return the rest +static int my_decimal_div_by_two(std::vector &digits) +{ + int carry = 0; + for (size_t i = 0; i < digits.size(); i++) { + assert(digits[i] < 10); + digits[i] += carry * 10; + carry = digits[i] % 2; + digits[i] /= 2; + } + return carry; +} + +// find the number of significant bits in a binary number (not including the sign bit) +static int my_ilog2(int x) +{ + int ret = 0; + while (x != 0 && x != -1) { + x = x >> 1; + ret++; + } + return ret; +} + +// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') +static void my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type) +{ + // all digits in string (MSB at index 0) + std::vector digits; + + while (*str) { + if ('0' <= *str && *str <= '9') + digits.push_back(*str - '0'); + else if ('a' <= *str && *str <= 'f') + digits.push_back(10 + *str - 'a'); + else if ('A' <= *str && *str <= 'F') + digits.push_back(10 + *str - 'A'); + else if (*str == 'x' || *str == 'X') + digits.push_back(0xf0); + else if (*str == 'z' || *str == 'Z') + digits.push_back(0xf1); + else if (*str == '?') + digits.push_back(0xf2); + str++; + } + + if (base == 10) { + data.clear(); + if (len_in_bits < 0) + len_in_bits = ceil(digits.size()/log10(2)); + for (int i = 0; i < len_in_bits; i++) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + return; + } + + int bits_per_digit = my_ilog2(base-1); + if (len_in_bits < 0) + len_in_bits = digits.size() * bits_per_digit; + + data.clear(); + data.resize(len_in_bits); + + for (int i = 0; i < len_in_bits; i++) { + int bitmask = 1 << (i % bits_per_digit); + int digitidx = digits.size() - (i / bits_per_digit) - 1; + if (digitidx < 0) { + if (i > 0 && (data[i-1] == RTLIL::Sz || data[i-1] == RTLIL::Sx || data[i-1] == RTLIL::Sa)) + data[i] = data[i-1]; + else + data[i] = RTLIL::S0; + } else if (digits[digitidx] == 0xf0) + data[i] = case_type == 'x' ? RTLIL::Sa : RTLIL::Sx; + else if (digits[digitidx] == 0xf1) + data[i] = case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz; + else if (digits[digitidx] == 0xf2) + data[i] = RTLIL::Sa; + else + data[i] = (digits[digitidx] & bitmask) ? RTLIL::S1 : RTLIL::S0; + } +} + +// convert the verilog code for a constant to an AST node +AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) +{ + const char *str = code.c_str(); + + // Strings + if (*str == '"') { + int len = strlen(str) - 2; + std::vector data; + data.reserve(len * 8); + for (int i = 0; i < len; i++) { + unsigned char ch = str[len - i]; + for (int j = 0; j < 8; j++) { + data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0); + ch = ch >> 1; + } + } + AstNode *ast = AstNode::mkconst_bits(data, false); + ast->str = code; + return ast; + } + + for (size_t i = 0; i < code.size(); i++) + if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n') + code.erase(code.begin()+(i--)); + str = code.c_str(); + + char *endptr; + long intval = strtol(str, &endptr, 10); + + // Simple 32 bit integer + if (*endptr == 0) + return AstNode::mkconst_int(intval, true); + + // variable length constant + if (str == endptr) + intval = -1; + + // The "'[bodh]" syntax + if (*endptr == '\'') + { + int len_in_bits = intval; + std::vector data; + bool is_signed = false; + if (*(endptr+1) == 's') { + is_signed = true; + endptr++; + } + switch (*(endptr+1)) + { + case 'b': + my_strtobin(data, endptr+2, len_in_bits, 2, case_type); + break; + case 'o': + my_strtobin(data, endptr+2, len_in_bits, 8, case_type); + break; + case 'd': + my_strtobin(data, endptr+2, len_in_bits, 10, case_type); + break; + case 'h': + my_strtobin(data, endptr+2, len_in_bits, 16, case_type); + break; + default: + goto error; + } + return AstNode::mkconst_bits(data, is_signed); + } + +error: + log_error("Value conversion failed: `%s'\n", code.c_str()); +} + diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l new file mode 100644 index 000000000..a269c072a --- /dev/null +++ b/frontends/verilog/lexer.l @@ -0,0 +1,264 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * A simple lexer for Verilog code. Non-preprocessor compiler directives are + * handled here. The preprocessor stuff is handled in preproc.cc. Everything + * else is left to the bison parser (see parser.y). + * + */ + +%{ + +#include "kernel/log.h" +#include "verilog_frontend.h" +#include "frontends/ast/ast.h" +#include "parser.tab.h" + +using namespace AST; +using namespace VERILOG_FRONTEND; + +namespace VERILOG_FRONTEND { + std::vector fn_stack; + std::vector ln_stack; + bool lexer_feature_defattr; +} + +%} + +%option yylineno +%option noyywrap +%option nounput +%option prefix="frontend_verilog_yy" + +%x COMMENT +%x STRING +%x SYNOPSYS_TRANSLATE_OFF +%x SYNOPSYS_FLAGS + +%% + +"`file_push "[^\n]* { + fn_stack.push_back(current_filename); + ln_stack.push_back(frontend_verilog_yyget_lineno()); + current_filename = yytext+11; + frontend_verilog_yyset_lineno(0); +} + +"`file_pop"[^\n]*\n { + current_filename = fn_stack.back(); + frontend_verilog_yyset_lineno(ln_stack.back()); +} + +"`file_notfound "[^\n]* { + log_error("Can't open include file `%s'!\n", yytext + 15); +} + +"`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ + +"`yosys_enable_defattr" lexer_feature_defattr = true; +"`yosys_disable_defattr" lexer_feature_defattr = false; + +"`"[a-zA-Z_$][a-zA-Z0-9_$]* { + frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); +} + +"module" { return TOK_MODULE; } +"endmodule" { return TOK_ENDMODULE; } +"function" { return TOK_FUNCTION; } +"endfunction" { return TOK_ENDFUNCTION; } +"task" { return TOK_TASK; } +"endtask" { return TOK_ENDTASK; } +"parameter" { return TOK_PARAMETER; } +"localparam" { return TOK_LOCALPARAM; } +"assign" { return TOK_ASSIGN; } +"always" { return TOK_ALWAYS; } +"initial" { return TOK_INITIAL; } +"begin" { return TOK_BEGIN; } +"end" { return TOK_END; } +"if" { return TOK_IF; } +"else" { return TOK_ELSE; } +"for" { return TOK_FOR; } +"posedge" { return TOK_POSEDGE; } +"negedge" { return TOK_NEGEDGE; } +"or" { return TOK_OR; } +"case" { return TOK_CASE; } +"casex" { return TOK_CASEX; } +"casez" { return TOK_CASEZ; } +"endcase" { return TOK_ENDCASE; } +"default" { return TOK_DEFAULT; } +"generate" { return TOK_GENERATE; } +"endgenerate" { return TOK_ENDGENERATE; } + +"input" { return TOK_INPUT; } +"output" { return TOK_OUTPUT; } +"inout" { return TOK_INOUT; } +"wire" { return TOK_WIRE; } +"reg" { return TOK_REG; } +"integer" { return TOK_INTEGER; } +"signed" { return TOK_SIGNED; } +"genvar" { return TOK_GENVAR; } + +[0-9]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_CONST; +} + +[0-9]*[ \t]*\'s?[bodh][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_CONST; +} + +\" { BEGIN(STRING); } +\\. { yymore(); } +\" { + BEGIN(0); + char *yystr = strdup(yytext); + yystr[strlen(yytext) - 1] = 0; + int i = 0, j = 0; + while (yystr[i]) { + if (yystr[i] == '\\' && yystr[i + 1]) { + i++; + if (yystr[i] == 'n') + yystr[i] = '\n'; + else if (yystr[i] == 't') + yystr[i] = '\t'; + else if ('0' <= yystr[i] && yystr[i] <= '7') { + yystr[i] = yystr[i] - '0'; + if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { + yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; + i++; + } + if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { + yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; + i++; + } + } + } + yystr[j++] = yystr[i++]; + } + yystr[j] = 0; + frontend_verilog_yylval.string = new std::string(yystr); + free(yystr); + return TOK_STRING; +} +. { yymore(); } + +and|nand|or|nor|xor|xnor|not|buf { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_PRIMITIVE; +} + +supply0 { return TOK_SUPPLY0; } +supply1 { return TOK_SUPPLY1; } + +"$"(display|time|stop|finish) { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_ID; +} + +"$signed" { return TOK_TO_SIGNED; } +"$unsigned" { return TOK_TO_UNSIGNED; } + +[a-zA-Z_$][a-zA-Z0-9_$]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + +"/*"[ \t]*synopsys[ \t]*translate_off[ \t]*"*/" { + log("Warning: Found one of those horrible `synopsys translate_off' comments.\n"); + log("It is strongly suggested to use `ifdef constructs instead!\n"); + BEGIN(SYNOPSYS_TRANSLATE_OFF); +} +. /* ignore synopsys translate_off body */ +\n /* ignore synopsys translate_off body */ +"/*"[ \t]*"synopsys"[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); } + +"/*"[ \t]*"synopsys"[ \t]+ { + BEGIN(SYNOPSYS_FLAGS); +} +full_case { + log("Warning: Found one of those horrible `synopsys full_case' comments.\n"); + log("It is strongly suggested to use verilog x-values and default branches instead!\n"); + return TOK_SYNOPSYS_FULL_CASE; +} +parallel_case { + log("Warning: Found one of those horrible `synopsys parallel_case' comments.\n"); + log("It is strongly suggested to use verilog `parallel_case' attributes instead!\n"); + return TOK_SYNOPSYS_PARALLEL_CASE; +} +. /* ignore everything else */ +"*/" { BEGIN(0); } + +"\\"[^ \t\r\n]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_ID; +} + +"(*" { return ATTR_BEGIN; } +"*)" { return ATTR_END; } + +"{*" { if (lexer_feature_defattr) return DEFATTR_BEGIN; else REJECT; } +"*}" { if (lexer_feature_defattr) return DEFATTR_END; else REJECT; } + +"**" { return OP_POW; } +"||" { return OP_LOR; } +"&&" { return OP_LAND; } +"==" { return OP_EQ; } +"!=" { return OP_NE; } +"<=" { return OP_LE; } +">=" { return OP_GE; } + + /* "~&" { return OP_NAND; } */ + /* "~|" { return OP_NOR; } */ +"~^" { return OP_XNOR; } +"^~" { return OP_XNOR; } + +"<<" { return OP_SHL; } +">>" { return OP_SHR; } +"<<<" { return OP_SSHL; } +">>>" { return OP_SSHR; } + +"/*" { BEGIN(COMMENT); } +. /* ignore comment body */ +\n /* ignore comment body */ +"*/" { BEGIN(0); } + +[ \t\r\n] /* ignore whitespaces */ +\\[\r\n] /* ignore continuation sequence */ +"//"[^\r\n]* /* ignore one-line comments */ +"#"[$a-zA-Z_0-9\.]+ /* ignore simulation timings */ + +. { return *yytext; } + +%% + +// this is a hack to avoid the 'yyinput defined but not used' error msgs +void *frontend_verilog_avoid_input_warnings() { + return (void*)&yyinput; +} + diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y new file mode 100644 index 000000000..7c12bd565 --- /dev/null +++ b/frontends/verilog/parser.y @@ -0,0 +1,1074 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * This is the actual bison parser for Verilog code. The AST ist created directly + * from the bison reduce functions here. Note that this code uses a few global + * variables to hold the state of the AST generator and therefore this parser is + * not reentrant. + * + */ + +%{ +#include +#include +#include "verilog_frontend.h" +#include "kernel/log.h" + +using namespace AST; +using namespace VERILOG_FRONTEND; + +namespace VERILOG_FRONTEND { + int port_counter; + std::map port_stubs; + std::map attr_list, default_attr_list; + std::map *albuf; + std::vector ast_stack; + struct AstNode *astbuf1, *astbuf2, *astbuf3; + struct AstNode *current_function_or_task; + struct AstNode *current_ast, *current_ast_mod; + int current_function_or_task_port_id; + std::vector case_type_stack; +} + +static void append_attr(AstNode *ast, std::map *al) +{ + for (auto &it : *al) { + if (ast->attributes.count(it.first) > 0) + delete ast->attributes[it.first]; + ast->attributes[it.first] = it.second; + } + delete al; +} + +static void append_attr_clone(AstNode *ast, std::map *al) +{ + for (auto &it : *al) { + if (ast->attributes.count(it.first) > 0) + delete ast->attributes[it.first]; + ast->attributes[it.first] = it.second->clone(); + } +} + +static void free_attr(std::map *al) +{ + for (auto &it : *al) + delete it.second; + delete al; +} + +%} + +%name-prefix="frontend_verilog_yy" + +%union { + std::string *string; + struct AstNode *ast; + std::map *al; + bool boolean; +} + +%token TOK_STRING TOK_ID TOK_CONST TOK_PRIMITIVE +%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END +%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM +%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG +%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL +%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR +%token TOK_POSEDGE TOK_NEGEDGE TOK_OR +%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT +%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK +%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR +%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE +%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED + +%type wire_type range expr basic_expr concat_list lvalue lvalue_concat_list +%type opt_label tok_prim_wrapper +%type opt_signed +%type attr + +// operator precedence from low to high +%left OP_LOR +%left OP_LAND +%left '|' +%left '^' OP_XNOR +%left '&' +%left OP_EQ OP_NE +%left '<' OP_LE OP_GE '>' +%left OP_SHL OP_SHR OP_SSHL OP_SSHR +%left '+' '-' +%left '*' '/' '%' +%left OP_POW +%right UNARY_OPS + +%expect 2 +%debug + +%% + +input: + module input | + defattr input | + /* empty */ { + for (auto &it : default_attr_list) + delete it.second; + default_attr_list.clear(); + }; + +attr: + { + for (auto &it : attr_list) + delete it.second; + attr_list.clear(); + for (auto &it : default_attr_list) + attr_list[it.first] = it.second->clone(); + } attr_opt { + std::map *al = new std::map; + al->swap(attr_list); + $$ = al; + }; + +attr_opt: + attr_opt ATTR_BEGIN opt_attr_list ATTR_END | + /* empty */; + +defattr: + DEFATTR_BEGIN { + for (auto &it : default_attr_list) + delete it.second; + default_attr_list.clear(); + for (auto &it : attr_list) + delete it.second; + attr_list.clear(); + } opt_attr_list { + default_attr_list = attr_list; + attr_list.clear(); + } DEFATTR_END; + +opt_attr_list: + attr_list | /* empty */; + +attr_list: + attr_assign | + attr_list ',' attr_assign; + +attr_assign: + TOK_ID { + if (attr_list.count(*$1) != 0) + delete attr_list[*$1]; + attr_list[*$1] = AstNode::mkconst_int(0, false, 0); + delete $1; + } | + TOK_ID '=' expr { + if (attr_list.count(*$1) != 0) + delete attr_list[*$1]; + attr_list[*$1] = $3; + delete $1; + }; + +module: + attr TOK_MODULE TOK_ID { + AstNode *mod = new AstNode(AST_MODULE); + current_ast->children.push_back(mod); + current_ast_mod = mod; + ast_stack.push_back(mod); + port_stubs.clear(); + port_counter = 0; + mod->str = *$3; + append_attr(mod, $1); + delete $3; + } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE { + if (port_stubs.size() != 0) + frontend_verilog_yyerror("Missing details for module port `%s'.", + port_stubs.begin()->first.c_str()); + ast_stack.pop_back(); + assert(ast_stack.size() == 0); + }; + +module_para_opt: + '#' '(' TOK_PARAMETER param_decl_list optional_comma ')' | /* empty */; + +module_args_opt: + '(' ')' | /* empty */ | '(' module_args optional_comma ')'; + +module_args: + module_arg | module_args ',' module_arg; + +optional_comma: + ',' | /* empty */; + +module_arg: + TOK_ID range { + if (port_stubs.count(*$1) != 0) + frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str()); + port_stubs[*$1] = ++port_counter; + if ($2 != NULL) + delete $2; + delete $1; + } | + attr wire_type range TOK_ID { + AstNode *node = $2; + node->str = *$4; + node->port_id = ++port_counter; + if ($3 != NULL) + node->children.push_back($3); + if (!node->is_input && !node->is_output) + frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str()); + if (node->is_reg && node->is_input && !node->is_output) + frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str()); + ast_stack.back()->children.push_back(node); + append_attr(node, $1); + delete $4; + }; + +wire_type: + { + astbuf3 = new AstNode(AST_WIRE); + } wire_type_token_list { + $$ = astbuf3; + }; + +wire_type_token_list: + wire_type_token | wire_type_token_list wire_type_token; + +wire_type_token: + TOK_INPUT { + astbuf3->is_input = true; + } | + TOK_OUTPUT { + astbuf3->is_output = true; + } | + TOK_INOUT { + astbuf3->is_input = true; + astbuf3->is_output = true; + } | + TOK_WIRE { + } | + TOK_REG { + astbuf3->is_reg = true; + } | + TOK_INTEGER { + astbuf3->is_reg = true; + astbuf3->range_left = 31; + astbuf3->range_right = 0; + } | + TOK_GENVAR { + astbuf3->type = AST_GENVAR; + astbuf3->is_reg = true; + astbuf3->range_left = 31; + astbuf3->range_right = 0; + } | + TOK_SIGNED { + astbuf3->is_signed = true; + }; + +range: + '[' expr ':' expr ']' { + $$ = new AstNode(AST_RANGE); + $$->children.push_back($2); + $$->children.push_back($4); + } | + '[' expr ']' { + $$ = new AstNode(AST_RANGE); + $$->children.push_back($2); + } | + /* empty */ { + $$ = NULL; + }; + +module_body: + module_body module_body_stmt | + /* empty */; + +module_body_stmt: + task_func_decl | param_decl | localparam_decl | wire_decl | assign_stmt | cell_stmt | + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr; + +task_func_decl: + TOK_TASK TOK_ID ';' { + current_function_or_task = new AstNode(AST_TASK); + current_function_or_task->str = *$2; + ast_stack.back()->children.push_back(current_function_or_task); + ast_stack.push_back(current_function_or_task); + current_function_or_task_port_id = 1; + delete $2; + } task_func_body TOK_ENDTASK { + current_function_or_task = NULL; + ast_stack.pop_back(); + } | + TOK_FUNCTION opt_signed range TOK_ID ';' { + current_function_or_task = new AstNode(AST_FUNCTION); + current_function_or_task->str = *$4; + ast_stack.back()->children.push_back(current_function_or_task); + ast_stack.push_back(current_function_or_task); + AstNode *outreg = new AstNode(AST_WIRE); + if ($3 != NULL) + outreg->children.push_back($3); + outreg->str = *$4; + outreg->is_signed = $2; + current_function_or_task->children.push_back(outreg); + current_function_or_task_port_id = 1; + delete $4; + } task_func_body TOK_ENDFUNCTION { + current_function_or_task = NULL; + ast_stack.pop_back(); + }; + +opt_signed: + TOK_SIGNED { + $$ = true; + } | + /* empty */ { + $$ = false; + }; + +task_func_body: + task_func_body wire_decl | + task_func_body behavioral_stmt | + /* empty */; + +param_decl: + TOK_PARAMETER param_decl_list ';'; + +param_decl_list: + single_param_decl | param_decl_list ',' single_param_decl; + +single_param_decl: + range TOK_ID '=' expr { + AstNode *node = new AstNode(AST_PARAMETER); + node->str = *$2; + node->children.push_back($4); + if ($1 != NULL) + node->children.push_back($1); + ast_stack.back()->children.push_back(node); + delete $2; + }; + +localparam_decl: + TOK_LOCALPARAM localparam_decl_list ';'; + +localparam_decl_list: + single_localparam_decl | localparam_decl_list ',' single_localparam_decl; + +single_localparam_decl: + range TOK_ID '=' expr { + AstNode *node = new AstNode(AST_LOCALPARAM); + node->str = *$2; + node->children.push_back($4); + if ($1 != NULL) + node->children.push_back($1); + ast_stack.back()->children.push_back(node); + delete $2; + }; + +wire_decl: + attr wire_type range { + albuf = $1; + astbuf1 = $2; + astbuf2 = $3; + if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { + if (astbuf2) { + frontend_verilog_yyerror("Syntax error."); + } else { + astbuf2 = new AstNode(AST_RANGE); + astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); + astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true)); + } + } + if (astbuf2 && astbuf2->children.size() != 2) + frontend_verilog_yyerror("Syntax error."); + } wire_name_list ';' { + delete astbuf1; + if (astbuf2 != NULL) + delete astbuf2; + free_attr(albuf); + } | + attr TOK_SUPPLY0 TOK_ID ';' { + ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); + ast_stack.back()->children.back()->str = *$3; + append_attr(ast_stack.back()->children.back(), $1); + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))); + ast_stack.back()->children.back()->children[0]->str = *$3; + delete $3; + } | + attr TOK_SUPPLY1 TOK_ID ';' { + ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); + ast_stack.back()->children.back()->str = *$3; + append_attr(ast_stack.back()->children.back(), $1); + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1))); + ast_stack.back()->children.back()->children[0]->str = *$3; + delete $3; + }; + +wire_name_list: + wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign; + +wire_name_and_opt_assign: + wire_name | + wire_name '=' expr { + if (!astbuf1->is_reg) { + AstNode *wire = new AstNode(AST_IDENTIFIER); + wire->str = ast_stack.back()->children.back()->str; + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3)); + } + }; + +wire_name: + TOK_ID range { + AstNode *node = astbuf1->clone(); + node->str = *$1; + append_attr_clone(node, albuf); + if (astbuf2 != NULL) + node->children.push_back(astbuf2->clone()); + if ($2 != NULL) { + if (node->is_input || node->is_output) + frontend_verilog_yyerror("Syntax error."); + if (!astbuf2) { + AstNode *rng = new AstNode(AST_RANGE); + rng->children.push_back(AstNode::mkconst_int(0, true)); + rng->children.push_back(AstNode::mkconst_int(0, true)); + node->children.push_back(rng); + } + node->type = AST_MEMORY; + node->children.push_back($2); + } + if (current_function_or_task == NULL) { + if (port_stubs.count(*$1) != 0) { + if (!node->is_input && !node->is_output) + frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str()); + if (node->is_reg && node->is_input && !node->is_output) + frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str()); + node->port_id = port_stubs[*$1]; + port_stubs.erase(*$1); + } else { + if (node->is_input || node->is_output) + frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); + } + ast_stack.back()->children.push_back(node); + } else { + if (node->is_input || node->is_output) + node->port_id = current_function_or_task_port_id++; + current_function_or_task->children.push_back(node); + } + delete $1; + }; + +assign_stmt: + TOK_ASSIGN assign_expr_list ';'; + +assign_expr_list: + assign_expr | assign_expr_list ',' assign_expr; + +assign_expr: + expr '=' expr { + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3)); + }; + +cell_stmt: + attr TOK_ID { + astbuf1 = new AstNode(AST_CELL); + append_attr(astbuf1, $1); + astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); + astbuf1->children[0]->str = *$2; + delete $2; + } cell_parameter_list_opt cell_list ';' { + delete astbuf1; + } | + attr tok_prim_wrapper { + astbuf1 = new AstNode(AST_PRIMITIVE); + astbuf1->str = *$2; + append_attr(astbuf1, $1); + delete $2; + } prim_list ';' { + delete astbuf1; + }; + +tok_prim_wrapper: + TOK_PRIMITIVE { + $$ = $1; + } | + TOK_OR { + $$ = new std::string("or"); + }; + +cell_list: + single_cell | + cell_list ',' single_cell; + +single_cell: + TOK_ID { + astbuf2 = astbuf1->clone(); + if (astbuf2->type != AST_PRIMITIVE) + astbuf2->str = *$1; + delete $1; + ast_stack.back()->children.push_back(astbuf2); + } '(' cell_port_list ')'; + +prim_list: + single_prim | + prim_list ',' single_prim; + +single_prim: + single_cell | + /* no name */ { + astbuf2 = astbuf1->clone(); + ast_stack.back()->children.push_back(astbuf2); + } '(' cell_port_list ')'; + +cell_parameter_list_opt: + '#' '(' cell_parameter_list ')' | /* empty */; + +cell_parameter_list: + /* empty */ | cell_parameter | + cell_parameter ',' cell_parameter_list; + +cell_parameter: + expr { + AstNode *node = new AstNode(AST_PARASET); + astbuf1->children.push_back(node); + node->children.push_back($1); + } | + '.' TOK_ID '(' expr ')' { + AstNode *node = new AstNode(AST_PARASET); + node->str = *$2; + astbuf1->children.push_back(node); + node->children.push_back($4); + delete $2; + }; + +cell_port_list: + /* empty */ | cell_port | + cell_port ',' cell_port_list | + /* empty */ ',' { + AstNode *node = new AstNode(AST_ARGUMENT); + astbuf2->children.push_back(node); + } cell_port_list; + +cell_port: + expr { + AstNode *node = new AstNode(AST_ARGUMENT); + astbuf2->children.push_back(node); + node->children.push_back($1); + } | + '.' TOK_ID '(' expr ')' { + AstNode *node = new AstNode(AST_ARGUMENT); + node->str = *$2; + astbuf2->children.push_back(node); + node->children.push_back($4); + delete $2; + } | + '.' TOK_ID '(' ')' { + AstNode *node = new AstNode(AST_ARGUMENT); + node->str = *$2; + astbuf2->children.push_back(node); + delete $2; + }; + +always_stmt: + attr TOK_ALWAYS { + AstNode *node = new AstNode(AST_ALWAYS); + append_attr(node, $1); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } always_cond { + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr TOK_INITIAL { + AstNode *node = new AstNode(AST_ALWAYS); + append_attr(node, $1); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + }; + +always_cond: + '@' '(' always_events ')' | + '@' '*' | + /* empty */; + +always_events: + always_event | + always_events TOK_OR always_event | + always_events ',' always_event; + +always_event: + TOK_POSEDGE expr { + AstNode *node = new AstNode(AST_POSEDGE); + ast_stack.back()->children.push_back(node); + node->children.push_back($2); + } | + TOK_NEGEDGE expr { + AstNode *node = new AstNode(AST_NEGEDGE); + ast_stack.back()->children.push_back(node); + node->children.push_back($2); + } | + expr { + AstNode *node = new AstNode(AST_EDGE); + ast_stack.back()->children.push_back(node); + node->children.push_back($1); + }; + +opt_label: + ':' TOK_ID { + $$ = $2; + } | + /* empty */ { + $$ = NULL; + }; + +simple_behavioral_stmt: + lvalue '=' expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); + ast_stack.back()->children.push_back(node); + } | + lvalue OP_LE expr { + AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $3); + ast_stack.back()->children.push_back(node); + }; + +// this production creates the obligatory if-else shift/reduce conflict +behavioral_stmt: + defattr | + simple_behavioral_stmt ';' | + TOK_ID attr { + AstNode *node = new AstNode(AST_TCALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $2); + } opt_arg_list ';'{ + ast_stack.pop_back(); + } | + attr TOK_BEGIN opt_label { + AstNode *node = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + } behavioral_stmt_list TOK_END opt_label { + if ($3 != NULL) + delete $3; + if ($7 != NULL) + delete $7; + ast_stack.pop_back(); + } | + attr TOK_FOR '(' { + AstNode *node = new AstNode(AST_FOR); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + } simple_behavioral_stmt ';' expr { + ast_stack.back()->children.push_back($7); + } ';' simple_behavioral_stmt ')' { + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr TOK_IF '(' expr ')' { + AstNode *node = new AstNode(AST_CASE); + AstNode *block = new AstNode(AST_BLOCK); + AstNode *cond = new AstNode(AST_COND, AstNode::mkconst_int(1, false, 1), block); + ast_stack.back()->children.push_back(node); + node->children.push_back(new AstNode(AST_REDUCE_BOOL, $4)); + node->children.push_back(cond); + ast_stack.push_back(node); + ast_stack.push_back(block); + append_attr(node, $1); + } behavioral_stmt optional_else { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr case_type '(' expr ')' { + AstNode *node = new AstNode(AST_CASE, $4); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + } opt_synopsys_attr case_body TOK_ENDCASE { + case_type_stack.pop_back(); + ast_stack.pop_back(); + }; + +case_type: + TOK_CASE { + case_type_stack.push_back(0); + } | + TOK_CASEX { + case_type_stack.push_back('x'); + } | + TOK_CASEZ { + case_type_stack.push_back('z'); + }; + +opt_synopsys_attr: + opt_synopsys_attr TOK_SYNOPSYS_FULL_CASE { + if (ast_stack.back()->attributes.count("\\full_case") == 0) + ast_stack.back()->attributes["\\full_case"] = AstNode::mkconst_int(0, false, 0); + } | + opt_synopsys_attr TOK_SYNOPSYS_PARALLEL_CASE { + if (ast_stack.back()->attributes.count("\\parallel_case") == 0) + ast_stack.back()->attributes["\\parallel_case"] = AstNode::mkconst_int(0, false, 0); + } | + /* empty */; + +behavioral_stmt_opt: + behavioral_stmt | + ';' ; + +behavioral_stmt_list: + behavioral_stmt_list behavioral_stmt | + /* empty */; + +optional_else: + TOK_ELSE { + AstNode *block = new AstNode(AST_BLOCK); + AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block); + ast_stack.pop_back(); + ast_stack.back()->children.push_back(cond); + ast_stack.push_back(block); + } behavioral_stmt | + /* empty */; + +case_body: + case_body case_item | + /* empty */; + +case_item: + { + AstNode *node = new AstNode(AST_COND); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } case_select { + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + case_type_stack.push_back(0); + } behavioral_stmt_opt { + case_type_stack.pop_back(); + ast_stack.pop_back(); + ast_stack.pop_back(); + }; + +case_select: + case_expr_list ':' | + TOK_DEFAULT; + +case_expr_list: + TOK_DEFAULT { + ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT)); + } | + expr { + ast_stack.back()->children.push_back($1); + } | + case_expr_list ',' expr { + ast_stack.back()->children.push_back($3); + }; + +lvalue: + TOK_ID range { + $$ = new AstNode(AST_IDENTIFIER); + $$->str = *$1; + if ($2) + $$->children.push_back($2); + delete $1; + } | + '{' lvalue_concat_list '}' { + $$ = $2; + }; + +lvalue_concat_list: + expr { + $$ = new AstNode(AST_CONCAT); + $$->children.push_back($1); + } | + expr ',' lvalue_concat_list { + $$ = $3; + $$->children.push_back($1); + }; + +opt_arg_list: + '(' arg_list optional_comma ')' | + /* empty */; + +arg_list: + arg_list2 | + /* empty */; + +arg_list2: + single_arg | + arg_list ',' single_arg; + +single_arg: + expr { + ast_stack.back()->children.push_back($1); + }; + +module_gen_body: + module_gen_body gen_stmt | + module_gen_body module_body_stmt | + /* empty */; + +// this production creates the obligatory if-else shift/reduce conflict +gen_stmt: + TOK_FOR '(' { + AstNode *node = new AstNode(AST_GENFOR); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } simple_behavioral_stmt ';' expr { + ast_stack.back()->children.push_back($6); + } ';' simple_behavioral_stmt ')' gen_stmt { + ast_stack.pop_back(); + } | + TOK_IF '(' expr ')' { + AstNode *node = new AstNode(AST_GENIF); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + ast_stack.back()->children.push_back($3); + } gen_stmt opt_gen_else { + ast_stack.pop_back(); + } | + TOK_BEGIN opt_label { + AstNode *node = new AstNode(AST_GENBLOCK); + node->str = $2 ? *$2 : std::string(); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } module_gen_body TOK_END opt_label { + if ($2 != NULL) + delete $2; + if ($6 != NULL) + delete $6; + ast_stack.pop_back(); + }; + +opt_gen_else: + TOK_ELSE gen_stmt | /* empty */; + +expr: + basic_expr { + $$ = $1; + } | + basic_expr '?' attr expr ':' expr { + $$ = new AstNode(AST_TERNARY); + $$->children.push_back($1); + $$->children.push_back($4); + $$->children.push_back($6); + append_attr($$, $3); + }; + +basic_expr: + TOK_CONST { + $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + delete $1; + } | + TOK_STRING { + std::string str = *$1; + std::vector data; + data.reserve(str.size() * 8); + for (size_t i = 0; i < str.size(); i++) { + unsigned char ch = str[str.size() - i - 1]; + for (int j = 0; j < 8; j++) { + data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0); + ch = ch >> 1; + } + } + $$ = AstNode::mkconst_bits(data, false); + $$->str = str; + delete $1; + } | + TOK_ID range { + $$ = new AstNode(AST_IDENTIFIER, $2); + $$->str = *$1; + delete $1; + } | + TOK_ID attr { + AstNode *node = new AstNode(AST_FCALL); + node->str = *$1; + delete $1; + ast_stack.push_back(node); + append_attr(node, $2); + } '(' arg_list optional_comma ')' { + $$ = ast_stack.back(); + ast_stack.pop_back(); + } | + TOK_TO_SIGNED attr '(' expr ')' { + $$ = new AstNode(AST_TO_SIGNED, $4); + append_attr($$, $2); + } | + TOK_TO_UNSIGNED attr '(' expr ')' { + $$ = new AstNode(AST_TO_UNSIGNED, $4); + append_attr($$, $2); + } | + '(' expr ')' { + $$ = $2; + } | + '{' concat_list '}' { + $$ = $2; + } | + '{' expr '{' expr '}' '}' { + $$ = new AstNode(AST_REPLICATE, $2, $4); + } | + '~' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_BIT_NOT, $3); + append_attr($$, $2); + } | + basic_expr '&' attr basic_expr { + $$ = new AstNode(AST_BIT_AND, $1, $4); + append_attr($$, $3); + } | + basic_expr '|' attr basic_expr { + $$ = new AstNode(AST_BIT_OR, $1, $4); + append_attr($$, $3); + } | + basic_expr '^' attr basic_expr { + $$ = new AstNode(AST_BIT_XOR, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_XNOR attr basic_expr { + $$ = new AstNode(AST_BIT_XNOR, $1, $4); + append_attr($$, $3); + } | + '&' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_AND, $3); + append_attr($$, $2); + } | + '|' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_OR, $3); + append_attr($$, $2); + } | + '^' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_XOR, $3); + append_attr($$, $2); + } | + OP_XNOR attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_XNOR, $3); + append_attr($$, $2); + } | + basic_expr OP_SHL attr basic_expr { + $$ = new AstNode(AST_SHIFT_LEFT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_SHR attr basic_expr { + $$ = new AstNode(AST_SHIFT_RIGHT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_SSHL attr basic_expr { + $$ = new AstNode(AST_SHIFT_SLEFT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_SSHR attr basic_expr { + $$ = new AstNode(AST_SHIFT_SRIGHT, $1, $4); + append_attr($$, $3); + } | + basic_expr '<' attr basic_expr { + $$ = new AstNode(AST_LT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_LE attr basic_expr { + $$ = new AstNode(AST_LE, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_EQ attr basic_expr { + $$ = new AstNode(AST_EQ, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_NE attr basic_expr { + $$ = new AstNode(AST_NE, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_GE attr basic_expr { + $$ = new AstNode(AST_GE, $1, $4); + append_attr($$, $3); + } | + basic_expr '>' attr basic_expr { + $$ = new AstNode(AST_GT, $1, $4); + append_attr($$, $3); + } | + basic_expr '+' attr basic_expr { + $$ = new AstNode(AST_ADD, $1, $4); + append_attr($$, $3); + } | + basic_expr '-' attr basic_expr { + $$ = new AstNode(AST_SUB, $1, $4); + append_attr($$, $3); + } | + basic_expr '*' attr basic_expr { + $$ = new AstNode(AST_MUL, $1, $4); + append_attr($$, $3); + } | + basic_expr '/' attr basic_expr { + $$ = new AstNode(AST_DIV, $1, $4); + append_attr($$, $3); + } | + basic_expr '%' attr basic_expr { + $$ = new AstNode(AST_MOD, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_POW attr basic_expr { + $$ = new AstNode(AST_POW, $1, $4); + append_attr($$, $3); + } | + '+' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_POS, $3); + append_attr($$, $2); + } | + '-' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_NEG, $3); + append_attr($$, $2); + } | + basic_expr OP_LAND attr basic_expr { + $$ = new AstNode(AST_LOGIC_AND, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_LOR attr basic_expr { + $$ = new AstNode(AST_LOGIC_OR, $1, $4); + append_attr($$, $3); + } | + '!' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_LOGIC_NOT, $3); + append_attr($$, $2); + }; + +concat_list: + expr { + $$ = new AstNode(AST_CONCAT, $1); + } | + expr ',' concat_list { + $$ = $3; + $$->children.push_back($1); + }; + diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc new file mode 100644 index 000000000..e6fdc1ffd --- /dev/null +++ b/frontends/verilog/preproc.cc @@ -0,0 +1,360 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * Ad-hoc implementation of a Verilog preprocessor. The directives `define, + * `include, `ifdef, `ifndef, `else and `endif are handled here. All other + * directives are handled by the lexer (see lexer.l). + * + */ + +#include "verilog_frontend.h" +#include "kernel/log.h" +#include +#include +#include +#include +#include + +static std::list output_code; +static std::list input_buffer; +static size_t input_buffer_charp; + +static void return_char(char ch) +{ + if (input_buffer_charp == 0) + input_buffer.push_front(std::string() + ch); + else + input_buffer.front()[--input_buffer_charp] = ch; +} + +static void insert_input(std::string str) +{ + if (input_buffer_charp != 0) { + input_buffer.front() = input_buffer.front().substr(input_buffer_charp); + input_buffer_charp = 0; + } + input_buffer.push_front(str); +} + +static char next_char() +{ + if (input_buffer.size() == 0) + return 0; + + assert(input_buffer_charp <= input_buffer.front().size()); + if (input_buffer_charp == input_buffer.front().size()) { + input_buffer_charp = 0; + input_buffer.pop_front(); + return next_char(); + } + + char ch = input_buffer.front()[input_buffer_charp++]; + return ch == '\r' ? next_char() : ch; +} + +static void skip_spaces() +{ + while (1) { + char ch = next_char(); + if (ch == 0) + break; + if (ch != ' ' && ch != '\t') { + return_char(ch); + break; + } + } +} + +static std::string next_token(bool pass_newline = false) +{ + std::string token; + + char ch = next_char(); + if (ch == 0) + return token; + + token += ch; + if (ch == '\n') { + if (pass_newline) { + output_code.push_back(token); + return ""; + } + return token; + } + + if (ch == ' ' || ch == '\t') + { + while ((ch = next_char()) != 0) { + if (ch != ' ' && ch != '\t') { + return_char(ch); + break; + } + token += ch; + } + } + else if (ch == '"') + { + while ((ch = next_char()) != 0) { + token += ch; + if (ch == '"') + break; + if (ch == '\\') { + if ((ch = next_char()) != 0) + token += ch; + } + } + } + else if (ch == '/') + { + if ((ch = next_char()) != 0) { + if (ch == '/') { + token += '*'; + char last_ch = 0; + while ((ch = next_char()) != 0) { + if (ch == '\n') { + return_char(ch); + break; + } + if (last_ch != '*' || ch != '/') { + token += ch; + last_ch = ch; + } + } + token += " */"; + } + else if (ch == '*') { + token += '*'; + int newline_count = 0; + char last_ch = 0; + while ((ch = next_char()) != 0) { + if (ch == '\n') { + newline_count++; + token += ' '; + } else + token += ch; + if (last_ch == '*' && ch == '/') + break; + last_ch = ch; + } + while (newline_count-- > 0) + return_char('\n'); + } + else + return_char(ch); + } + } + else + { + const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789"; + while ((ch = next_char()) != 0) { + if (strchr(ok, ch) == NULL) { + return_char(ch); + break; + } + token += ch; + } + } + + return token; +} + +static void input_file(FILE *f, std::string filename) +{ + char buffer[513]; + int rc; + + insert_input(""); + auto it = input_buffer.begin(); + + input_buffer.insert(it, "`file_push " + filename + "\n"); + while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) { + buffer[rc] = 0; + input_buffer.insert(it, buffer); + } + input_buffer.insert(it, "`file_pop\n"); +} + +static std::string define_to_feature(std::string defname) +{ + if (defname == "__YOSYS_ENABLE_DEFATTR__") + return "defattr"; + return std::string(); +} + +std::string frontend_verilog_preproc(FILE *f, std::string filename) +{ + std::map defines_map; + int ifdef_fail_level = 0; + + output_code.clear(); + input_buffer.clear(); + input_buffer_charp = 0; + + input_file(f, filename); + defines_map["__YOSYS__"] = "1"; + + while (!input_buffer.empty()) + { + std::string tok = next_token(); + // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); + + if (tok == "`endif") { + if (ifdef_fail_level > 0) + ifdef_fail_level--; + continue; + } + + if (tok == "`else") { + if (ifdef_fail_level == 0) + ifdef_fail_level = 1; + else if (ifdef_fail_level == 1) + ifdef_fail_level = 0; + continue; + } + + if (tok == "`ifdef") { + skip_spaces(); + std::string name = next_token(true); + if (ifdef_fail_level > 0 || defines_map.count(name) == 0) + ifdef_fail_level++; + continue; + } + + if (tok == "`ifndef") { + skip_spaces(); + std::string name = next_token(true); + if (ifdef_fail_level > 0 || defines_map.count(name) != 0) + ifdef_fail_level++; + continue; + } + + if (ifdef_fail_level > 0) { + if (tok == "\n") + output_code.push_back(tok); + continue; + } + + if (tok == "`include") { + skip_spaces(); + std::string fn = next_token(true); + while (1) { + size_t pos = fn.find('"'); + if (pos == std::string::npos) + break; + if (pos == 0) + fn = fn.substr(1); + else + fn = fn.substr(0, pos) + fn.substr(pos+1); + } + FILE *fp = fopen(fn.c_str(), "r"); + if (fp == NULL && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { + std::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn; + fp = fopen(fn2.c_str(), "r"); + } + if (fp != NULL) { + input_file(fp, fn); + fclose(fp); + } else + output_code.push_back("`file_notfound " + fn + "\n"); + continue; + } + + if (tok == "`define") { + std::string name, value; + skip_spaces(); + name = next_token(true); + if (!define_to_feature(name).empty()) + output_code.push_back("`yosys_enable_" + define_to_feature(name)); + skip_spaces(); + int newline_count = 0; + while (!tok.empty()) { + tok = next_token(); + if (tok == "\n") { + return_char('\n'); + break; + } + if (tok == "\\") { + char ch = next_char(); + if (ch == '\n') { + value += " "; + newline_count++; + } else { + value += std::string("\\"); + return_char(ch); + } + } else + value += tok; + } + while (newline_count-- > 0) + return_char('\n'); + // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); + defines_map[name] = value; + continue; + } + + if (tok == "`undef") { + std::string name; + skip_spaces(); + name = next_token(true); + if (!define_to_feature(name).empty()) + output_code.push_back("`yosys_disable_" + define_to_feature(name)); + // printf("undef: >>%s<<\n", name.c_str()); + defines_map.erase(name); + continue; + } + + if (tok == "`timescale") { + std::string name; + skip_spaces(); + while (!tok.empty() && tok != "\n") + tok = next_token(true); + if (tok == "\n") + return_char('\n'); + continue; + } + + if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) { + // printf("expand: >>%s<< -> >>%s<<\n", tok.c_str(), defines_map[tok.substr(1)].c_str()); + insert_input(defines_map[tok.substr(1)]); + continue; + } + + output_code.push_back(tok); + } + + std::string output; + for (auto &str : output_code) + output += str; + + output_code.clear(); + input_buffer.clear(); + input_buffer_charp = 0; + + return output; +} + diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc new file mode 100644 index 000000000..c18233793 --- /dev/null +++ b/frontends/verilog/verilog_frontend.cc @@ -0,0 +1,148 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + */ + +#include "verilog_frontend.h" +#include "kernel/register.h" +#include "kernel/log.h" +#include "kernel/sha1.h" +#include +#include +#include + +using namespace VERILOG_FRONTEND; + +// use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL + +struct VerilogFrontend : public Frontend { + VerilogFrontend() : Frontend("verilog") { } + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + bool flag_dump_ast = false; + bool flag_dump_ast_diff = false; + bool flag_dump_vlog = false; + bool flag_nolatches = false; + bool flag_nomem2reg = false; + bool flag_ppdump = false; + bool flag_nopp = false; + frontend_verilog_yydebug = false; + + log_header("Executing Verilog-2005 frontend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-dump_ast") { + flag_dump_ast = true; + continue; + } + if (arg == "-dump_ast_diff") { + flag_dump_ast = true; + flag_dump_ast_diff = true; + continue; + } + if (arg == "-dump_vlog") { + flag_dump_vlog = true; + continue; + } + if (arg == "-yydebug") { + frontend_verilog_yydebug = true; + continue; + } + if (arg == "-nolatches") { + flag_nolatches = true; + continue; + } + if (arg == "-nomem2reg") { + flag_nomem2reg = true; + continue; + } + if (arg == "-ppdump") { + flag_ppdump = true; + continue; + } + if (arg == "-nopp") { + flag_nopp = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + log("Parsing Verilog input from `%s' to AST representation.\n", filename.c_str()); + + AST::current_filename = filename; + AST::set_line_num = &frontend_verilog_yyset_lineno; + AST::get_line_num = &frontend_verilog_yyget_lineno; + + current_ast = new AST::AstNode(AST::AST_DESIGN); + + FILE *fp = f; + std::string code_after_preproc; + + if (!flag_nopp) { + code_after_preproc = frontend_verilog_preproc(f, filename); + if (flag_ppdump) + log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str()); + fp = fmemopen((void*)code_after_preproc.c_str(), code_after_preproc.size(), "r"); + } + + lexer_feature_defattr = false; + + frontend_verilog_yyset_lineno(1); + frontend_verilog_yyrestart(fp); + frontend_verilog_yyparse(); + frontend_verilog_yylex_destroy(); + + AST::process(design, current_ast, flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg); + + if (!flag_nopp) + fclose(fp); + + delete current_ast; + current_ast = NULL; + + log("Successfully finished Verilog frontend.\n"); + } +} VerilogFrontend; + +// the yyerror function used by bison to report parser errors +void frontend_verilog_yyerror(char const *fmt, ...) +{ + va_list ap; + char buffer[1024]; + char *p = buffer; + p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ", + AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); + va_start(ap, fmt); + p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); + va_end(ap); + p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); + log_error("%s", buffer); + exit(1); +} + diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h new file mode 100644 index 000000000..808edfc7d --- /dev/null +++ b/frontends/verilog/verilog_frontend.h @@ -0,0 +1,62 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + */ + +#ifndef VERILOG_FRONTEND_H +#define VERILOG_FRONTEND_H + +#include "kernel/rtlil.h" +#include "frontends/ast/ast.h" +#include +#include + +namespace VERILOG_FRONTEND +{ + // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser + extern struct AST::AstNode *current_ast; + + // this function converts a Verilog constant to an AST_CONSTANT node + AST::AstNode *const2ast(std::string code, char case_type = 0); + + // lexer state variables + extern bool lexer_feature_defattr; +} + +// the pre-processor +std::string frontend_verilog_preproc(FILE *f, std::string filename); + +// the usual bison/flex stuff +extern int frontend_verilog_yydebug; +int frontend_verilog_yylex(void); +void frontend_verilog_yyerror(char const *fmt, ...); +void frontend_verilog_yyrestart(FILE *f); +int frontend_verilog_yyparse(void); +int frontend_verilog_yylex_destroy(void); +int frontend_verilog_yyget_lineno(void); +void frontend_verilog_yyset_lineno (int); + +#endif diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h new file mode 100644 index 000000000..aaefa50fc --- /dev/null +++ b/kernel/bitpattern.h @@ -0,0 +1,143 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef BITPATTERN_H +#define BITPATTERN_H + +#include "kernel/log.h" +#include "kernel/rtlil.h" + +struct BitPatternPool +{ + int width; + typedef std::vector bits_t; + std::set pool; + + BitPatternPool(RTLIL::SigSpec sig) + { + width = sig.width; + if (width > 0) { + std::vector pattern(width); + sig.optimize(); + for (int i = 0; i < width; i++) { + RTLIL::SigSpec s = sig.extract(i, 1); + s.optimize(); + assert(s.chunks.size() == 1); + if (s.chunks[0].wire == NULL && s.chunks[0].data.bits[0] <= RTLIL::State::S1) + pattern[i] = s.chunks[0].data.bits[0]; + else + pattern[i] = RTLIL::State::Sa; + } + pool.insert(pattern); + } + } + + BitPatternPool(int width) + { + this->width = width; + if (width > 0) { + std::vector pattern(width); + for (int i = 0; i < width; i++) + pattern[i] = RTLIL::State::Sa; + pool.insert(pattern); + } + } + + bits_t sig2bits(RTLIL::SigSpec sig) + { + sig.optimize(); + assert(sig.is_fully_const()); + assert(sig.chunks.size() == 1); + bits_t bits = sig.chunks[0].data.bits; + for (auto &b : bits) + if (b > RTLIL::State::S1) + b = RTLIL::State::Sa; + return bits; + } + + bool match(bits_t a, bits_t b) + { + assert(int(a.size()) == width); + assert(int(b.size()) == width); + for (int i = 0; i < width; i++) + if (a[i] <= RTLIL::State::S1 && b[i] <= RTLIL::State::S1 && a[i] != b[i]) + return false; + return true; + } + + bool has_any(RTLIL::SigSpec sig) + { + bits_t bits = sig2bits(sig); + for (auto &it : pool) + if (match(it, bits)) + return true; + return false; + } + + bool has_all(RTLIL::SigSpec sig) + { + bits_t bits = sig2bits(sig); + for (auto &it : pool) + if (match(it, bits)) { + for (int i = 0; i < width; i++) + if (bits[i] > RTLIL::State::S1 && it[i] <= RTLIL::State::S1) + goto next_pool_entry; + return true; + next_pool_entry:; + } + return false; + } + + bool take(RTLIL::SigSpec sig) + { + bool status = false; + bits_t bits = sig2bits(sig); + std::vector pattern_list; + for (auto &it : pool) + if (match(it, bits)) + pattern_list.push_back(it); + for (auto pattern : pattern_list) { + pool.erase(pattern); + for (int i = 0; i < width; i++) { + if (pattern[i] != RTLIL::State::Sa || bits[i] == RTLIL::State::Sa) + continue; + bits_t new_pattern = pattern; + new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1; + pool.insert(new_pattern); + } + status = true; + } + return status; + } + + bool take_all() + { + if (pool.empty()) + return false; + pool.clear(); + return true; + } + + bool empty() + { + return pool.empty(); + } +}; + +#endif diff --git a/kernel/calc.cc b/kernel/calc.cc new file mode 100644 index 000000000..f31fed7d0 --- /dev/null +++ b/kernel/calc.cc @@ -0,0 +1,392 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/rtlil.h" +#include "bigint/BigIntegerLibrary.hh" +#include + +static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos) +{ + BigInteger result = 0, this_bit = 1; + for (size_t i = 0; i < val.bits.size(); i++) { + if (val.bits[i] == RTLIL::State::S1) { + if (as_signed && i+1 == val.bits.size()) + result -= this_bit; + else + result += this_bit; + } + else if (val.bits[i] != RTLIL::State::S0) { + if (undef_bit_pos < 0) + undef_bit_pos = i; + } + this_bit *= 2; + } + return result; +} + +static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_bit_pos) +{ + BigUnsigned mag = val.getMagnitude(); + RTLIL::Const result(0, result_len); + + if (!mag.isZero()) + { + if (val.getSign() < 0) + { + mag--; + for (int i = 0; i < result_len; i++) + result.bits[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1; + } + else + { + for (int i = 0; i < result_len; i++) + result.bits[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0; + } + } + + if (undef_bit_pos >= 0) + for (int i = undef_bit_pos; i < result_len; i++) + result.bits[i] = RTLIL::State::Sx; + + return result; +} + +static RTLIL::State logic_and(RTLIL::State a, RTLIL::State b) +{ + if (a == RTLIL::State::S0) return RTLIL::State::S0; + if (b == RTLIL::State::S0) return RTLIL::State::S0; + if (a != RTLIL::State::S1) return RTLIL::State::Sx; + if (b != RTLIL::State::S1) return RTLIL::State::Sx; + return RTLIL::State::S1; +} + +static RTLIL::State logic_or(RTLIL::State a, RTLIL::State b) +{ + if (a == RTLIL::State::S1) return RTLIL::State::S1; + if (b == RTLIL::State::S1) return RTLIL::State::S1; + if (a != RTLIL::State::S0) return RTLIL::State::Sx; + if (b != RTLIL::State::S0) return RTLIL::State::Sx; + return RTLIL::State::S0; +} + +static RTLIL::State logic_xor(RTLIL::State a, RTLIL::State b) +{ + if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx; + if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx; + return a != b ? RTLIL::State::S1 : RTLIL::State::S0; +} + +static RTLIL::State logic_xnor(RTLIL::State a, RTLIL::State b) +{ + if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx; + if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx; + return a == b ? RTLIL::State::S1 : RTLIL::State::S0; +} + +RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) +{ + if (result_len < 0) + result_len = arg1.bits.size(); + + RTLIL::Const result(RTLIL::State::Sx, result_len); + for (size_t i = 0; i < size_t(result_len); i++) { + if (i >= arg1.bits.size()) + result.bits[i] = RTLIL::State::S0; + else if (arg1.bits[i] == RTLIL::State::S0) + result.bits[i] = RTLIL::State::S1; + else if (arg1.bits[i] == RTLIL::State::S1) + result.bits[i] = RTLIL::State::S0; + } + + return result; +} + +static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State), + const RTLIL::Const &arg1, const RTLIL::Const &arg2, int result_len = -1) +{ + if (result_len < 0) + result_len = std::max(arg1.bits.size(), arg2.bits.size()); + + RTLIL::Const result(RTLIL::State::Sx, result_len); + for (size_t i = 0; i < size_t(result_len); i++) { + RTLIL::State a = i < arg1.bits.size() ? arg1.bits[i] : RTLIL::State::S0; + RTLIL::State b = i < arg2.bits.size() ? arg2.bits[i] : RTLIL::State::S0; + result.bits[i] = logic_func(a, b); + } + + return result; +} + +RTLIL::Const RTLIL::const_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return logic_wrapper(logic_and, arg1, arg2, result_len); +} + +RTLIL::Const RTLIL::const_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return logic_wrapper(logic_or, arg1, arg2, result_len); +} + +RTLIL::Const RTLIL::const_xor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return logic_wrapper(logic_xor, arg1, arg2, result_len); +} + +RTLIL::Const RTLIL::const_xnor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return logic_wrapper(logic_xnor, arg1, arg2, result_len); +} + +static RTLIL::Const logic_reduce_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State), const RTLIL::Const &arg1) +{ + RTLIL::State temp = RTLIL::State::S0; + + for (size_t i = 0; i < arg1.bits.size(); i++) + temp = logic_func(temp, arg1.bits[i]); + + return RTLIL::Const(temp); +} + +RTLIL::Const RTLIL::const_reduce_and(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int) +{ + return logic_reduce_wrapper(logic_and, arg1); +} + +RTLIL::Const RTLIL::const_reduce_or(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int) +{ + return logic_reduce_wrapper(logic_or, arg1); +} + +RTLIL::Const RTLIL::const_reduce_xor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int) +{ + return logic_reduce_wrapper(logic_xor, arg1); +} + +RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int) +{ + return logic_reduce_wrapper(logic_xnor, arg1); +} + +RTLIL::Const RTLIL::const_reduce_bool(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int) +{ + return logic_reduce_wrapper(logic_or, arg1); +} + +RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int) +{ + int undef_bit_pos_a = -1; + BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); + + if (a.isZero()) { + if (undef_bit_pos_a >= 0) + return RTLIL::Const(RTLIL::State::Sx); + return RTLIL::Const(RTLIL::State::S1); + } + + return RTLIL::Const(RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos_a = -1, undef_bit_pos_b = -1; + BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); + BigInteger b = const2big(arg2, signed2, undef_bit_pos_b); + + if (a.isZero() || b.isZero()) { + if (undef_bit_pos_a >= 0 && undef_bit_pos_b >= 0) + return RTLIL::Const(RTLIL::State::Sx); + return RTLIL::Const(RTLIL::State::S0); + } + + return RTLIL::Const(RTLIL::State::S1); +} + +RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos_a = -1, undef_bit_pos_b = -1; + BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); + BigInteger b = const2big(arg2, signed2, undef_bit_pos_b); + + if (a.isZero() && b.isZero()) { + if (undef_bit_pos_a >= 0 || undef_bit_pos_b >= 0) + return RTLIL::Const(RTLIL::State::Sx); + return RTLIL::Const(RTLIL::State::S0); + } + + return RTLIL::Const(RTLIL::State::S1); +} + +static RTLIL::Const const_shift(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool sign_ext, int direction, int result_len) +{ + int undef_bit_pos = -1; + BigInteger offset = const2big(arg2, false, undef_bit_pos) * direction; + + if (result_len < 0) + result_len = arg1.bits.size(); + + RTLIL::Const result(RTLIL::State::Sx, result_len); + if (undef_bit_pos >= 0) + return result; + + for (int i = 0; i < result_len; i++) { + BigInteger pos = BigInteger(i) + offset; + if (pos < 0) + result.bits[i] = RTLIL::State::S0; + else if (pos >= arg1.bits.size()) + result.bits[i] = sign_ext ? arg1.bits.back() : RTLIL::State::S0; + else + result.bits[i] = arg1.bits[pos.toInt()]; + } + + return result; +} + +RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return const_shift(arg1, arg2, false, -1, result_len); +} + +RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return const_shift(arg1, arg2, false, +1, result_len); +} + +RTLIL::Const RTLIL::const_sshl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return const_shift(arg1, arg2, true, -1, result_len); +} + +RTLIL::Const RTLIL::const_sshr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool, int result_len) +{ + return const_shift(arg1, arg2, true, +1, result_len); +} + +RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos = -1; + bool y = const2big(arg1, signed1, undef_bit_pos) < const2big(arg2, signed2, undef_bit_pos); + return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos = -1; + bool y = const2big(arg1, signed1, undef_bit_pos) <= const2big(arg2, signed2, undef_bit_pos); + return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos = -1; + bool y = const2big(arg1, signed1, undef_bit_pos) == const2big(arg2, signed2, undef_bit_pos); + return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos = -1; + bool y = const2big(arg1, signed1, undef_bit_pos) != const2big(arg2, signed2, undef_bit_pos); + return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos = -1; + bool y = const2big(arg1, signed1, undef_bit_pos) >= const2big(arg2, signed2, undef_bit_pos); + return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int) +{ + int undef_bit_pos = -1; + bool y = const2big(arg1, signed1, undef_bit_pos) > const2big(arg2, signed2, undef_bit_pos); + return RTLIL::Const(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); +} + +RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) +{ + int undef_bit_pos = -1; + BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); +} + +RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) +{ + int undef_bit_pos = -1; + BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); +} + +RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) +{ + int undef_bit_pos = -1; + BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); +} + +RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) +{ + int undef_bit_pos = -1; + BigInteger y = const2big(arg1, signed1, undef_bit_pos) / const2big(arg2, signed2, undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); +} + +RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) +{ + int undef_bit_pos = -1; + BigInteger y = const2big(arg1, signed1, undef_bit_pos) % const2big(arg2, signed2, undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); +} + +RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) +{ + int undef_bit_pos = -1; + + BigInteger a = const2big(arg1, signed1, undef_bit_pos); + BigInteger b = const2big(arg2, signed2, undef_bit_pos); + BigInteger y = 1; + + if (b < 0 || a == 0) { + y = 0; + } else { + while (b > 0) { + y = y * a; + if (y.getLength() > 0x10000) { + undef_bit_pos = 0; + break; + } + b--; + } + } + + return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); +} + +RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) +{ + RTLIL::Const zero(RTLIL::State::S0, 1); + return RTLIL::const_add(zero, arg1, false, signed1, result_len); +} + +RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) +{ + RTLIL::Const zero(RTLIL::State::S0, 1); + return RTLIL::const_sub(zero, arg1, false, signed1, result_len); +} + diff --git a/kernel/celltypes.h b/kernel/celltypes.h new file mode 100644 index 000000000..a13cbf32c --- /dev/null +++ b/kernel/celltypes.h @@ -0,0 +1,210 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef CELLTYPES_H +#define CELLTYPES_H + +#include +#include +#include + +struct CellTypes +{ + std::set cell_types; + + void setup_internals() + { + cell_types.insert("$not"); + cell_types.insert("$pos"); + cell_types.insert("$neg"); + cell_types.insert("$and"); + cell_types.insert("$or"); + cell_types.insert("$xor"); + cell_types.insert("$xnor"); + cell_types.insert("$reduce_and"); + cell_types.insert("$reduce_or"); + cell_types.insert("$reduce_xor"); + cell_types.insert("$reduce_xnor"); + cell_types.insert("$reduce_bool"); + cell_types.insert("$shl"); + cell_types.insert("$shr"); + cell_types.insert("$sshl"); + cell_types.insert("$sshr"); + cell_types.insert("$lt"); + cell_types.insert("$le"); + cell_types.insert("$eq"); + cell_types.insert("$ne"); + cell_types.insert("$ge"); + cell_types.insert("$gt"); + cell_types.insert("$add"); + cell_types.insert("$sub"); + cell_types.insert("$mul"); + cell_types.insert("$div"); + cell_types.insert("$mod"); + cell_types.insert("$pow"); + cell_types.insert("$logic_not"); + cell_types.insert("$logic_and"); + cell_types.insert("$logic_or"); + cell_types.insert("$mux"); + cell_types.insert("$pmux"); + cell_types.insert("$safe_pmux"); + } + + void setup_internals_mem() + { + cell_types.insert("$dff"); + cell_types.insert("$adff"); + cell_types.insert("$memrd"); + cell_types.insert("$memwr"); + cell_types.insert("$mem"); + cell_types.insert("$fsm"); + } + + void setup_stdcells() + { + cell_types.insert("$_INV_"); + cell_types.insert("$_AND_"); + cell_types.insert("$_OR_"); + cell_types.insert("$_XOR_"); + cell_types.insert("$_MUX_"); + } + + void setup_stdcells_mem() + { + cell_types.insert("$_DFF_N_"); + cell_types.insert("$_DFF_P_"); + cell_types.insert("$_DFF_NN0_"); + cell_types.insert("$_DFF_NN1_"); + cell_types.insert("$_DFF_NP0_"); + cell_types.insert("$_DFF_NP1_"); + cell_types.insert("$_DFF_PN0_"); + cell_types.insert("$_DFF_PN1_"); + cell_types.insert("$_DFF_PP0_"); + cell_types.insert("$_DFF_PP1_"); + } + + void clear() + { + cell_types.clear(); + } + + bool cell_known(std::string type) + { + return cell_types.count(type) > 0; + } + + bool cell_output(std::string type, std::string port) + { + if (!cell_known(type)) + return false; + if (port == "\\Y" || port == "\\Q" || port == "\\RD_DATA") + return true; + if (type == "$memrd" && port == "\\DATA") + return true; + if (type == "$fsm" && port == "\\CTRL_OUT") + return true; + return false; + } + + bool cell_input(std::string type, std::string port) + { + if (!cell_known(type)) + return false; + return !cell_output(type, port); + } + + static RTLIL::Const eval(std::string type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) + { +#define HANDLE_CELL_TYPE(_t) if (type == "$" #_t) return const_ ## _t(arg1, arg2, signed1, signed2, result_len); + HANDLE_CELL_TYPE(not) + HANDLE_CELL_TYPE(and) + HANDLE_CELL_TYPE(or) + HANDLE_CELL_TYPE(xor) + HANDLE_CELL_TYPE(xnor) + HANDLE_CELL_TYPE(reduce_and) + HANDLE_CELL_TYPE(reduce_or) + HANDLE_CELL_TYPE(reduce_xor) + HANDLE_CELL_TYPE(reduce_xnor) + HANDLE_CELL_TYPE(reduce_bool) + HANDLE_CELL_TYPE(logic_not) + HANDLE_CELL_TYPE(logic_and) + HANDLE_CELL_TYPE(logic_or) + HANDLE_CELL_TYPE(shl) + HANDLE_CELL_TYPE(shr) + HANDLE_CELL_TYPE(sshl) + HANDLE_CELL_TYPE(sshr) + HANDLE_CELL_TYPE(lt) + HANDLE_CELL_TYPE(le) + HANDLE_CELL_TYPE(eq) + HANDLE_CELL_TYPE(ne) + HANDLE_CELL_TYPE(ge) + HANDLE_CELL_TYPE(gt) + HANDLE_CELL_TYPE(add) + HANDLE_CELL_TYPE(sub) + HANDLE_CELL_TYPE(mul) + HANDLE_CELL_TYPE(div) + HANDLE_CELL_TYPE(mod) + HANDLE_CELL_TYPE(pow) + HANDLE_CELL_TYPE(pos) + HANDLE_CELL_TYPE(neg) +#undef HANDLE_CELL_TYPE + + if (type == "$_INV_") + return const_not(arg1, arg2, false, false, 1); + if (type == "$_AND_") + return const_and(arg1, arg2, false, false, 1); + if (type == "$_OR_") + return const_or(arg1, arg2, false, false, 1); + if (type == "$_XOR_") + return const_xor(arg1, arg2, false, false, 1); + + assert(!"Called CellType.eval() with unsupported cell type!"); + abort(); + } + + static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2) + { + bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool(); + bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool(); + int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1; + return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len); + } + + static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &sel) + { + if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux" || cell->type == "$_MUX_") { + RTLIL::Const ret = arg1; + for (size_t i = 0; i < sel.bits.size(); i++) + if (sel.bits[i] == RTLIL::State::S1) { + std::vector bits(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size()); + ret = RTLIL::Const(bits); + } + return ret; + } + + assert(sel.bits.size() == 0); + bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool(); + bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool(); + int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1; + return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len); + } +}; + +#endif + diff --git a/kernel/consteval.h b/kernel/consteval.h new file mode 100644 index 000000000..d48771fea --- /dev/null +++ b/kernel/consteval.h @@ -0,0 +1,198 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef CONSTEVAL_H +#define CONSTEVAL_H + +#include "kernel/rtlil.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" + +struct ConstEval +{ + RTLIL::Module *module; + SigMap assign_map; + SigMap values_map; + SigPool stop_signals; + SigSet sig2driver; + std::set busy; + std::vector stack; + + ConstEval(RTLIL::Module *module) : module(module), assign_map(module) + { + CellTypes ct; + ct.setup_internals(); + ct.setup_stdcells(); + + for (auto &it : module->cells) { + if (!ct.cell_known(it.second->type)) + continue; + for (auto &it2 : it.second->connections) + if (ct.cell_output(it.second->type, it2.first)) + sig2driver.insert(assign_map(it2.second), it.second); + } + } + + void clear() + { + values_map.clear(); + stop_signals.clear(); + } + + void push() + { + stack.push_back(values_map); + } + + void pop() + { + values_map.swap(stack.back()); + stack.pop_back(); + } + + void set(RTLIL::SigSpec sig, RTLIL::Const value) + { + assign_map.apply(sig); +#ifndef NDEBUG + RTLIL::SigSpec current_val = values_map(sig); + current_val.expand(); + for (size_t i = 0; i < current_val.chunks.size(); i++) { + RTLIL::SigChunk &chunk = current_val.chunks[i]; + assert(chunk.wire != NULL || chunk.data.bits[0] == value.bits[i]); + } +#endif + values_map.add(sig, RTLIL::SigSpec(value)); + } + + void stop(RTLIL::SigSpec sig) + { + assign_map.apply(sig); + stop_signals.add(sig); + } + + bool eval(RTLIL::Cell *cell, RTLIL::SigSpec &undef) + { + RTLIL::SigSpec sig_a, sig_b, sig_s, sig_y; + bool ignore_sig_a = false, ignore_sig_b = false; + int sig_b_shift = -1; + + assert(cell->connections.count("\\Y") > 0); + sig_y = values_map(assign_map(cell->connections["\\Y"])); + if (sig_y.is_fully_const()) + return true; + + if (cell->connections.count("\\S") > 0) { + sig_s = cell->connections["\\S"]; + if (!eval(sig_s, undef, cell)) + return false; + } + + if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux" || cell->type == "$_MUX_") { + bool found_collision = false; + for (int i = 0; i < sig_s.width; i++) + if (sig_s.extract(i, 1).as_bool()) { + if (sig_b_shift >= 0) + found_collision = true; + sig_b_shift = i; + ignore_sig_a = true; + if (cell->type != "$safe_pmux") + break; + } + if (found_collision) { + sig_b_shift = -1; + ignore_sig_a = false; + } + if (sig_b_shift < 0) + ignore_sig_b = true; + } + + if (!ignore_sig_a && cell->connections.count("\\A") > 0) { + sig_a = cell->connections["\\A"]; + if (!eval(sig_a, undef, cell)) + return false; + } + + if (!ignore_sig_b && cell->connections.count("\\B") > 0) { + sig_b = cell->connections["\\B"]; + if (sig_b_shift >= 0) + sig_b = sig_b.extract(sig_y.width*sig_b_shift, sig_y.width); + if (!eval(sig_b, undef, cell)) + return false; + } + + if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux" || cell->type == "$_MUX_") + set(sig_y, sig_s.as_bool() ? sig_b.as_const() : sig_a.as_const()); + else + set(sig_y, CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const())); + + return true; + } + + bool eval(RTLIL::SigSpec &sig, RTLIL::SigSpec &undef, RTLIL::Cell *busy_cell = NULL) + { + assign_map.apply(sig); + values_map.apply(sig); + + if (sig.is_fully_const()) + return true; + + if (stop_signals.check_any(sig)) { + undef = stop_signals.extract(sig); + return false; + } + + if (busy_cell) { + if (busy.count(busy_cell) > 0) { + undef = sig; + return false; + } + busy.insert(busy_cell); + } + + std::set driver_cells; + sig2driver.find(sig, driver_cells); + for (auto cell : driver_cells) { + if (!eval(cell, undef)) { + if (busy_cell) + busy.erase(busy_cell); + return false; + } + } + + if (busy_cell) + busy.erase(busy_cell); + + values_map.apply(sig); + if (sig.is_fully_const()) + return true; + + for (size_t i = 0; i < sig.chunks.size(); i++) + if (sig.chunks[i].wire != NULL) + undef.append(sig.chunks[i]); + return false; + } + + bool eval(RTLIL::SigSpec &sig) + { + RTLIL::SigSpec undef; + return eval(sig, undef); + } +}; + +#endif diff --git a/kernel/driver.cc b/kernel/driver.cc new file mode 100644 index 000000000..c49bf6575 --- /dev/null +++ b/kernel/driver.cc @@ -0,0 +1,253 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include +#include + +#include "kernel/rtlil.h" +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +static void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command) +{ + if (command == "auto") { + if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v") + command = "verilog"; + else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") + command = "ilang"; + else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys") + command = "script"; + else if (filename == "-") + command = "ilang"; + else + log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str()); + } + + if (command == "script") { + log("\n-- Executing script file `%s' --\n", filename.c_str()); + FILE *f = fopen(filename.c_str(), "r"); + if (f == NULL) + log_error("Can;t open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno)); + char buffer[4096]; + while (fgets(buffer, 4096, f) != NULL) { + Pass::call(design, buffer); + design->check(); + } + fclose(f); + if (backend_command != NULL && *backend_command == "auto") + *backend_command = ""; + return; + } + + if (filename == "-") { + log("\n-- Parsing stdin using frontend `%s' --\n", command.c_str()); + } else { + log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str()); + } + + Frontend::frontend_call(design, NULL, filename, command); + design->check(); +} + +static void run_pass(std::string command, RTLIL::Design *design) +{ + log("\n-- Running pass `%s' --\n", command.c_str()); + + Pass::call(design, command); + design->check(); +} + +static void run_backend(std::string filename, std::string command, RTLIL::Design *design) +{ + if (command == "auto") { + if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v") + command = "verilog"; + else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") + command = "ilang"; + else if (filename == "-") + command = "ilang"; + else + log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str()); + } + + if (filename == "-") { + log("\n-- Writing to stdout using backend `%s' --\n", command.c_str()); + } else { + log("\n-- Writing to `%s' using backend `%s' --\n", filename.c_str(), command.c_str()); + } + + Backend::backend_call(design, NULL, filename, command); + design->check(); +} + +static char *readline_cmd_generator(const char *text, int state) +{ + static std::map::iterator it; + static int len; + + if (!state) { + it = REGISTER_INTERN::pass_register.begin(); + len = strlen(text); + } + + for (; it != REGISTER_INTERN::pass_register.end(); it++) { + if (it->first.substr(0, len) == text) + return strdup((it++)->first.c_str()); + } + return NULL; +} + +static char **readline_completion(const char *text, int start, int) +{ + if (start == 0) + return rl_completion_matches(text, readline_cmd_generator); + return NULL; +} + +static const char *create_prompt(RTLIL::Design *design) +{ + static char buffer[100]; + std::string str = "\nyosys"; + if (!design->selected_active_module.empty()) + str += stringf(" [%s]", design->selected_active_module.c_str()); + if (!design->selection_stack.back().full_selection) { + if (design->selected_active_module.empty()) + str += "*"; + else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 || + design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0) + str += "*"; + } + snprintf(buffer, 100, "%s> ", str.c_str()); + return buffer; +} + +int main(int argc, char **argv) +{ + std::string frontend_command = "auto"; + std::string backend_command = "auto"; + std::vector passes_commands; + std::string output_filename = "-"; + std::string scriptfile = ""; + bool got_output_filename = false; + + RTLIL::Design *design = new RTLIL::Design; + design->selection_stack.push_back(RTLIL::Selection()); + log_push(); + + int opt; + while ((opt = getopt(argc, argv, "f:b:o:p:l:qts:")) != -1) + { + switch (opt) + { + case 'f': + frontend_command = optarg; + break; + case 'b': + backend_command = optarg; + break; + case 'p': + passes_commands.push_back(optarg); + break; + case 'o': + output_filename = optarg; + got_output_filename = true; + break; + case 'l': + log_files.push_back(fopen(optarg, "wt")); + if (log_files.back() == NULL) { + fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg); + exit(1); + } + break; + case 'q': + log_errfile = stderr; + break; + case 't': + log_time = true; + break; + case 's': + scriptfile = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-q] [-t] [-l logfile] [-o ] [-f ] [-s ] [-p [-p ..]] [-b ] [ [..]]\n", argv[0]); + exit(1); + } + } + + if (log_errfile == NULL) + log_files.push_back(stderr); + + if (optind == argc && passes_commands.size() == 0 && scriptfile.empty()) + { + log_cmd_error_throw = true; + + rl_readline_name = "yosys"; + rl_attempted_completion_function = readline_completion; + + char *command = NULL; + while ((command = readline(create_prompt(design))) != NULL) + { + if (command[strspn(command, " \t\r\n")] == 0) + continue; + add_history(command); + + try { + assert(design->selection_stack.size() == 1); + Pass::call(design, command); + } catch (int) { + while (design->selection_stack.size() > 1) + design->selection_stack.pop_back(); + log_reset_stack(); + } + } + + if (!got_output_filename) + backend_command = ""; + log_cmd_error_throw = false; + } + + while (optind < argc) + run_frontend(argv[optind++], frontend_command, design, output_filename == "-" ? &backend_command : NULL); + + if (!scriptfile.empty()) + run_frontend(scriptfile, "script", design, output_filename == "-" ? &backend_command : NULL); + + for (auto it = passes_commands.begin(); it != passes_commands.end(); it++) + run_pass(*it, design); + + if (!backend_command.empty()) + run_backend(output_filename, backend_command, design); + + delete design; + + log("\nREADY.\n"); + log_pop(); + + for (auto f : log_files) + if (f != stderr) + fclose(f); + log_errfile = NULL; + log_files.clear(); + + return 0; +} + diff --git a/kernel/log.cc b/kernel/log.cc new file mode 100644 index 000000000..9bf8705e3 --- /dev/null +++ b/kernel/log.cc @@ -0,0 +1,197 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "backends/ilang/ilang_backend.h" + +#include +#include +#include +#include +#include +#include +#include + +std::vector log_files; +FILE *log_errfile = NULL; +bool log_time = false; +bool log_cmd_error_throw = false; + +std::vector header_count; +std::list string_buf; + +static struct timeval initial_tv = { 0, 0 }; +static bool next_print_log = false; + +std::string stringf(const char *fmt, ...) +{ + std::string string; + char *str = NULL; + va_list ap; + + va_start(ap, fmt); + if (vasprintf(&str, fmt, ap) < 0) + str = NULL; + va_end(ap); + + if (str != NULL) { + string = str; + free(str); + } + + return string; +} + +void logv(const char *format, va_list ap) +{ + if (log_time) { + while (format[0] == '\n' && format[1] != 0) { + format++; + log("\n"); + } + if (next_print_log || initial_tv.tv_sec == 0) { + next_print_log = false; + struct timeval tv; + gettimeofday(&tv, NULL); + if (initial_tv.tv_sec == 0) + initial_tv = tv; + if (tv.tv_usec < initial_tv.tv_usec) { + tv.tv_sec--; + tv.tv_usec += 1000000; + } + tv.tv_sec -= initial_tv.tv_sec; + tv.tv_usec -= initial_tv.tv_usec; + log("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec)); + } + if (format[0] && format[strlen(format)-1] == '\n') + next_print_log = true; + } + + for (auto f : log_files) { + va_list aq; + va_copy(aq, ap); + vfprintf(f, format, aq); + va_end(aq); + } +} + +void logv_header(const char *format, va_list ap) +{ + log("\n"); + if (header_count.size() > 0) + header_count.back()++; + for (int c : header_count) + log("%d.", c); + log(" "); + logv(format, ap); + log_flush(); +} + +void logv_error(const char *format, va_list ap) +{ + log("ERROR: "); + logv(format, ap); + if (log_errfile != NULL) { + fprintf(log_errfile, "ERROR: "); + vfprintf(log_errfile, format, ap); + } + log_flush(); + exit(1); +} + +void log(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + logv(format, ap); + va_end(ap); +} + +void log_header(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + logv_header(format, ap); + va_end(ap); +} + +void log_error(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + logv_error(format, ap); +} + +void log_cmd_error(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + + if (log_cmd_error_throw) { + log("ERROR: "); + logv(format, ap); + log_flush(); + throw 0; + } + + logv_error(format, ap); +} + +void log_push() +{ + header_count.push_back(0); +} + +void log_pop() +{ + header_count.pop_back(); + string_buf.clear(); + log_flush(); +} + +void log_reset_stack() +{ + while (header_count.size() > 1) + header_count.pop_back(); + string_buf.clear(); + log_flush(); +} + +void log_flush() +{ + for (auto f : log_files) + fflush(f); +} + +const char *log_signal(const RTLIL::SigSpec &sig, bool autoint) +{ + char *ptr; + size_t size; + + FILE *f = open_memstream(&ptr, &size); + ILANG_BACKEND::dump_sigspec(f, sig, autoint); + fputc(0, f); + fclose(f); + + string_buf.push_back(ptr); + free(ptr); + + return string_buf.back().c_str(); +} + diff --git a/kernel/log.h b/kernel/log.h new file mode 100644 index 000000000..9023854d0 --- /dev/null +++ b/kernel/log.h @@ -0,0 +1,51 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef LOG_H +#define LOG_H + +#include "kernel/rtlil.h" +#include +#include + +extern std::vector log_files; +extern FILE *log_errfile; +extern bool log_time; +extern bool log_cmd_error_throw; + +std::string stringf(const char *fmt, ...); + +void logv(const char *format, va_list ap); +void logv_header(const char *format, va_list ap); +void logv_error(const char *format, va_list ap) __attribute__ ((noreturn)); + +void log(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +void log_header(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +void log_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))) __attribute__ ((noreturn)); +void log_cmd_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))) __attribute__ ((noreturn)); + +void log_push(); +void log_pop(); + +void log_reset_stack(); +void log_flush(); + +const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true); + +#endif diff --git a/kernel/register.cc b/kernel/register.cc new file mode 100644 index 000000000..fb70c80d9 --- /dev/null +++ b/kernel/register.cc @@ -0,0 +1,288 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "register.h" +#include "log.h" +#include +#include + +using namespace REGISTER_INTERN; + +namespace REGISTER_INTERN { + std::map frontend_register; + std::map pass_register; + std::map backend_register; +} + +std::vector Frontend::next_args; + +Pass::Pass(std::string name) : pass_name(name) +{ + assert(pass_register.count(name) == 0); + pass_register[name] = this; +} + +Pass::~Pass() +{ + pass_register.erase(pass_name); +} + +void Pass::help() +{ + log("No help message for this command.\n"); +} + +void Pass::cmd_log_args(const std::vector &args) +{ + if (args.size() <= 1) + return; + log("Full command line:"); + for (size_t i = 0; i < args.size(); i++) + log(" %s", args[i].c_str()); + log("\n"); +} + +void Pass::cmd_error(const std::vector &args, size_t argidx, std::string msg) +{ + std::string command_text; + int error_pos = 0; + + for (size_t i = 0; i < args.size(); i++) { + if (i < argidx) + error_pos += args[i].size() + 1; + command_text = command_text + (command_text.empty() ? "" : " ") + args[i]; + } + + log("\nSyntax error in command `%s':\n", command_text.c_str()); + help(); + + log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n", + msg.c_str(), command_text.c_str(), error_pos, ""); +} + +void Pass::extra_args(std::vector args, size_t argidx, RTLIL::Design *) +{ + for (; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + + if (arg.substr(0, 1) == "-") + cmd_error(args, argidx, "Unkown option or option in arguments."); + cmd_error(args, argidx, "Extra argument."); + } + cmd_log_args(args); +} + +void Pass::call(RTLIL::Design *design, std::string command) +{ + std::vector args; + char *s = strdup(command.c_str()); + for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + args.push_back(p); + free(s); + call(design, args); +} + +void Pass::call(RTLIL::Design *design, std::vector args) +{ + if (args.size() == 0 || args[0][0] == '#') + return; + if (pass_register.count(args[0]) == 0) + log_cmd_error("No such command: %s\n", args[0].c_str()); + + size_t orig_sel_stack_pos = design->selection_stack.size(); + pass_register[args[0]]->execute(args, design); + while (design->selection_stack.size() > orig_sel_stack_pos) + design->selection_stack.pop_back(); +} + +Frontend::Frontend(std::string name) : Pass("read_"+name), frontend_name(name) +{ + assert(frontend_register.count(name) == 0); + frontend_register[name] = this; +} + +Frontend::~Frontend() +{ + frontend_register.erase(frontend_name); +} + +void Frontend::execute(std::vector args, RTLIL::Design *design) +{ + assert(next_args.empty()); + do { + FILE *f = NULL; + next_args.clear(); + execute(f, std::string(), args, design); + args = next_args; + fclose(f); + } while (!args.empty()); +} + +void Frontend::extra_args(FILE *&f, std::string &filename, std::vector args, size_t argidx) +{ + bool called_with_fp = f != NULL; + + next_args.clear(); + for (; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + + if (arg.substr(0, 1) == "-") + cmd_error(args, argidx, "Unkown option or option in arguments."); + if (f != NULL) + cmd_error(args, argidx, "Extra filename argument in direct file mode."); + + filename = arg; + f = fopen(filename.c_str(), "r"); + if (f == NULL) + log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno)); + + if (argidx+1 < args.size()) { + next_args.insert(next_args.begin(), args.begin(), args.begin()+argidx); + next_args.insert(next_args.begin()+argidx, args.begin()+argidx+1, args.end()); + args.erase(args.begin()+argidx+1, args.end()); + } + break; + } + if (f == NULL) + cmd_error(args, argidx, "No filename given."); + + if (called_with_fp) + args.push_back(filename); + args[0] = pass_name; + cmd_log_args(args); +} + +void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command) +{ + std::vector args; + char *s = strdup(command.c_str()); + for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + args.push_back(p); + free(s); + frontend_call(design, f, filename, args); +} + +void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector args) +{ + if (args.size() == 0) + return; + if (frontend_register.count(args[0]) == 0) + log_cmd_error("No such frontend: %s\n", args[0].c_str()); + + if (f != NULL) { + frontend_register[args[0]]->execute(f, filename, args, design); + } else if (filename == "-") { + frontend_register[args[0]]->execute(stdin, "", args, design); + } else { + if (!filename.empty()) + args.push_back(filename); + frontend_register[args[0]]->execute(args, design); + } +} + +Backend::Backend(std::string name) : Pass("write_"+name), backend_name(name) +{ + assert(backend_register.count(name) == 0); + backend_register[name] = this; +} + +Backend::~Backend() +{ + backend_register.erase(backend_name); +} + +void Backend::execute(std::vector args, RTLIL::Design *design) +{ + FILE *f = NULL; + execute(f, std::string(), args, design); + if (f != stdout) + fclose(f); +} + +void Backend::extra_args(FILE *&f, std::string &filename, std::vector args, size_t argidx) +{ + bool called_with_fp = f != NULL; + + for (; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + + if (arg.substr(0, 1) == "-" && arg != "-") + cmd_error(args, argidx, "Unkown option or option in arguments."); + if (f != NULL) + cmd_error(args, argidx, "Extra filename argument in direct file mode."); + + if (arg == "-") { + filename = ""; + f = stdout; + continue; + } + + filename = arg; + f = fopen(filename.c_str(), "w"); + if (f == NULL) + log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); + } + + if (called_with_fp) + args.push_back(filename); + args[0] = pass_name; + cmd_log_args(args); + + if (f == NULL) { + filename = ""; + f = stdout; + } +} + +void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command) +{ + std::vector args; + char *s = strdup(command.c_str()); + for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + args.push_back(p); + free(s); + backend_call(design, f, filename, args); +} + +void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector args) +{ + if (args.size() == 0) + return; + if (backend_register.count(args[0]) == 0) + log_cmd_error("No such backend: %s\n", args[0].c_str()); + + size_t orig_sel_stack_pos = design->selection_stack.size(); + + if (f != NULL) { + backend_register[args[0]]->execute(f, filename, args, design); + } else if (filename == "-") { + backend_register[args[0]]->execute(stdout, "", args, design); + } else { + if (!filename.empty()) + args.push_back(filename); + backend_register[args[0]]->execute(args, design); + } + + while (design->selection_stack.size() > orig_sel_stack_pos) + design->selection_stack.pop_back(); +} + diff --git a/kernel/register.h b/kernel/register.h new file mode 100644 index 000000000..713d468e3 --- /dev/null +++ b/kernel/register.h @@ -0,0 +1,80 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef REGISTER_H +#define REGISTER_H + +#include "kernel/rtlil.h" +#include +#include +#include +#include + +struct Pass +{ + std::string pass_name; + Pass(std::string name); + virtual ~Pass(); + virtual void help(); + virtual void execute(std::vector args, RTLIL::Design *design) = 0; + + void cmd_log_args(const std::vector &args); + void cmd_error(const std::vector &args, size_t argidx, std::string msg); + void extra_args(std::vector args, size_t argidx, RTLIL::Design *design); + + static void call(RTLIL::Design *design, std::string command); + static void call(RTLIL::Design *design, std::vector args); +}; + +struct Frontend : Pass +{ + std::string frontend_name; + Frontend(std::string name); + virtual ~Frontend(); + virtual void execute(std::vector args, RTLIL::Design *design); + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) = 0; + + static std::vector next_args; + void extra_args(FILE *&f, std::string &filename, std::vector args, size_t argidx); + + static void frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command); + static void frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector args); +}; + +struct Backend : Pass +{ + std::string backend_name; + Backend(std::string name); + virtual ~Backend(); + virtual void execute(std::vector args, RTLIL::Design *design); + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) = 0; + + void extra_args(FILE *&f, std::string &filename, std::vector args, size_t argidx); + + static void backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command); + static void backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector args); +}; + +namespace REGISTER_INTERN { + extern std::map pass_register; + extern std::map frontend_register; + extern std::map backend_register; +} + +#endif diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc new file mode 100644 index 000000000..c97e2e455 --- /dev/null +++ b/kernel/rtlil.cc @@ -0,0 +1,1081 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/rtlil.h" +#include +#include + +int RTLIL::autoidx = 1; + +RTLIL::Const::Const(std::string str) : str(str) +{ + for (size_t i = 0; i < str.size(); i++) { + unsigned char ch = str[i]; + for (int j = 0; j < 8; j++) { + bits.push_back((ch & 1) != 0 ? RTLIL::S1 : RTLIL::S0); + ch = ch >> 1; + } + } +} + +RTLIL::Const::Const(int val, int width) +{ + for (int i = 0; i < width; i++) { + bits.push_back((val & 1) != 0 ? RTLIL::S1 : RTLIL::S0); + val = val >> 1; + } +} + +RTLIL::Const::Const(RTLIL::State bit, int width) +{ + for (int i = 0; i < width; i++) + bits.push_back(bit); +} + +bool RTLIL::Const::operator <(const RTLIL::Const &other) const +{ + if (bits.size() != other.bits.size()) + return bits.size() < other.bits.size(); + for (size_t i = 0; i < bits.size(); i++) + if (bits[i] != other.bits[i]) + return bits[i] < other.bits[i]; + return false; +} + +bool RTLIL::Const::operator ==(const RTLIL::Const &other) const +{ + return bits == other.bits; +} + +bool RTLIL::Const::operator !=(const RTLIL::Const &other) const +{ + return bits != other.bits; +} + +bool RTLIL::Const::as_bool() const +{ + for (size_t i = 0; i < bits.size(); i++) + if (bits[i] == RTLIL::S1) + return true; + return false; +} + +int RTLIL::Const::as_int() const +{ + int ret = 0; + for (size_t i = 0; i < bits.size() && i < 32; i++) + if (bits[i] == RTLIL::S1) + ret |= 1 << i; + return ret; +} + +std::string RTLIL::Const::as_string() const +{ + std::string ret; + for (size_t i = bits.size(); i > 0; i--) + switch (bits[i-1]) { + case S0: ret += "0"; break; + case S1: ret += "1"; break; + case Sx: ret += "x"; break; + case Sz: ret += "z"; break; + case Sa: ret += "-"; break; + case Sm: ret += "m"; break; + } + return ret; +} + +bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) +{ + if (full_selection) + return true; + if (selected_modules.count(mod_name) > 0) + return true; + if (selected_members.count(mod_name) > 0) + return true; + return false; +} + +bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) +{ + if (full_selection) + return true; + if (selected_modules.count(mod_name) > 0) + return true; + return false; +} + +bool RTLIL::Selection::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) +{ + if (full_selection) + return true; + if (selected_modules.count(mod_name) > 0) + return true; + if (selected_members.count(mod_name) > 0) + if (selected_members[mod_name].count(memb_name) > 0) + return true; + return false; +} + +void RTLIL::Selection::optimize(RTLIL::Design *design) +{ + if (full_selection) { + selected_modules.clear(); + selected_members.clear(); + return; + } + + std::vector del_list, add_list; + + del_list.clear(); + for (auto mod_name : selected_modules) { + if (design->modules.count(mod_name) == 0) + del_list.push_back(mod_name); + selected_members.erase(mod_name); + } + for (auto mod_name : del_list) + selected_modules.erase(mod_name); + + del_list.clear(); + for (auto &it : selected_members) + if (design->modules.count(it.first) == 0) + del_list.push_back(it.first); + for (auto mod_name : del_list) + selected_members.erase(mod_name); + + for (auto &it : selected_members) { + del_list.clear(); + for (auto memb_name : it.second) + if (design->modules[it.first]->count_id(memb_name) == 0) + del_list.push_back(memb_name); + for (auto memb_name : del_list) + it.second.erase(memb_name); + } + + del_list.clear(); + add_list.clear(); + for (auto &it : selected_members) + if (it.second.size() == 0) + del_list.push_back(it.first); + else if (it.second.size() == design->modules[it.first]->wires.size() + design->modules[it.first]->memories.size() + + design->modules[it.first]->cells.size() + design->modules[it.first]->processes.size()) + add_list.push_back(it.first); + for (auto mod_name : del_list) + selected_members.erase(mod_name); + for (auto mod_name : add_list) { + selected_members.erase(mod_name); + selected_modules.insert(mod_name); + } + + if (selected_modules.size() == design->modules.size()) { + full_selection = true; + selected_modules.clear(); + selected_members.clear(); + } +} + +RTLIL::Design::~Design() +{ + for (auto it = modules.begin(); it != modules.end(); it++) + delete it->second; +} + +void RTLIL::Design::check() +{ +#ifndef NDEBUG + for (auto &it : modules) { + assert(it.first == it.second->name); + assert(it.first.size() > 0 && (it.first[0] == '\\' || it.first[0] == '$')); + it.second->check(); + } +#endif +} + +void RTLIL::Design::optimize() +{ + for (auto &it : modules) + it.second->optimize(); + for (auto &it : selection_stack) + it.optimize(this); + for (auto &it : selection_vars) + it.second.optimize(this); +} + +bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) +{ + if (!selected_active_module.empty() && mod_name != selected_active_module) + return false; + if (selection_stack.size() == 0) + return true; + return selection_stack.back().selected_module(mod_name); +} + +bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) +{ + if (!selected_active_module.empty() && mod_name != selected_active_module) + return false; + if (selection_stack.size() == 0) + return true; + return selection_stack.back().selected_whole_module(mod_name); +} + +bool RTLIL::Design::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) +{ + if (!selected_active_module.empty() && mod_name != selected_active_module) + return false; + if (selection_stack.size() == 0) + return true; + return selection_stack.back().selected_member(mod_name, memb_name); +} + +RTLIL::Module::~Module() +{ + for (auto it = wires.begin(); it != wires.end(); it++) + delete it->second; + for (auto it = memories.begin(); it != memories.end(); it++) + delete it->second; + for (auto it = cells.begin(); it != cells.end(); it++) + delete it->second; + for (auto it = processes.begin(); it != processes.end(); it++) + delete it->second; +} + +RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, std::map) +{ + assert(!"Called derive() from module base class."); + abort(); +} + +void RTLIL::Module::update_auto_wires(std::map) +{ + assert(!"Called update_auto_wires() from module base class."); +} + +size_t RTLIL::Module::count_id(RTLIL::IdString id) +{ + return wires.count(id) + memories.count(id) + cells.count(id) + processes.count(id); +} + +void RTLIL::Module::check() +{ +#ifndef NDEBUG + for (auto &it : wires) { + assert(it.first == it.second->name); + assert(it.first.size() > 0 && (it.first[0] == '\\' || it.first[0] == '$')); + assert(it.second->width >= 0); + assert(it.second->port_id >= 0); + for (auto &it2 : it.second->attributes) { + assert(it2.first.size() > 0 && (it2.first[0] == '\\' || it2.first[0] == '$')); + } + } + + for (auto &it : memories) { + assert(it.first == it.second->name); + assert(it.first.size() > 0 && (it.first[0] == '\\' || it.first[0] == '$')); + assert(it.second->width >= 0); + assert(it.second->size >= 0); + for (auto &it2 : it.second->attributes) { + assert(it2.first.size() > 0 && (it2.first[0] == '\\' || it2.first[0] == '$')); + } + } + + for (auto &it : cells) { + assert(it.first == it.second->name); + assert(it.first.size() > 0 && (it.first[0] == '\\' || it.first[0] == '$')); + assert(it.second->type.size() > 0 && (it.second->type[0] == '\\' || it.second->type[0] == '$')); + for (auto &it2 : it.second->connections) { + assert(it2.first.size() > 0 && (it2.first[0] == '\\' || it2.first[0] == '$')); + it2.second.check(); + } + for (auto &it2 : it.second->attributes) { + assert(it2.first.size() > 0 && (it2.first[0] == '\\' || it2.first[0] == '$')); + } + for (auto &it2 : it.second->parameters) { + assert(it2.first.size() > 0 && (it2.first[0] == '\\' || it2.first[0] == '$')); + } + } + + for (auto &it : processes) { + assert(it.first == it.second->name); + assert(it.first.size() > 0 && (it.first[0] == '\\' || it.first[0] == '$')); + // FIXME: More checks here.. + } + + for (auto &it : connections) { + assert(it.first.width == it.second.width); + it.first.check(); + it.second.check(); + } + + for (auto &it : attributes) { + assert(it.first.size() > 0 && (it.first[0] == '\\' || it.first[0] == '$')); + } +#endif +} + +void RTLIL::Module::optimize() +{ + for (auto &it : cells) + it.second->optimize(); + for (auto &it : processes) + it.second->optimize(); + for (auto &it : connections) { + it.first.optimize(); + it.second.optimize(); + } +} + +void RTLIL::Module::add(RTLIL::Wire *wire) { + assert(!wire->name.empty()); + assert(count_id(wire->name) == 0); + wires[wire->name] = wire; +} + +void RTLIL::Module::add(RTLIL::Cell *cell) { + assert(!cell->name.empty()); + assert(count_id(cell->name) == 0); + cells[cell->name] = cell; +} + +RTLIL::Wire::Wire() +{ + width = 1; + start_offset = 0; + port_id = 0; + port_input = false; + port_output = false; + auto_width = false; +} + +RTLIL::Memory::Memory() +{ + width = 1; + size = 0; +} + +void RTLIL::Cell::optimize() +{ + for (auto &it : connections) + it.second.optimize(); +} + +RTLIL::SigChunk::SigChunk() +{ + wire = NULL; + width = 0; + offset = 0; +} + +RTLIL::SigChunk::SigChunk(const RTLIL::Const &data) +{ + wire = NULL; + this->data = data; + width = data.bits.size(); + offset = 0; +} + +RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire, int width, int offset) +{ + this->wire = wire; + this->width = width >= 0 ? width : wire->width; + this->offset = offset; +} + +RTLIL::SigChunk::SigChunk(const std::string &str) +{ + wire = NULL; + data = RTLIL::Const(str); + width = data.bits.size(); + offset = 0; +} + +RTLIL::SigChunk::SigChunk(int val, int width) +{ + wire = NULL; + data = RTLIL::Const(val, width); + this->width = data.bits.size(); + offset = 0; +} + +RTLIL::SigChunk::SigChunk(RTLIL::State bit, int width) +{ + wire = NULL; + data = RTLIL::Const(bit, width); + this->width = data.bits.size(); + offset = 0; +} + +RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const +{ + RTLIL::SigChunk ret; + if (wire) { + ret.wire = wire; + ret.offset = this->offset + offset; + ret.width = length; + } else { + for (int i = 0; i < length; i++) + ret.data.bits.push_back(data.bits[offset+i]); + ret.width = length; + } + return ret; +} + +bool RTLIL::SigChunk::operator <(const RTLIL::SigChunk &other) const +{ + if (wire && other.wire) + if (wire->name != other.wire->name) + return wire->name < other.wire->name; + if (wire != other.wire) + return wire < other.wire; + + if (offset != other.offset) + return offset < other.offset; + + if (width != other.width) + return width < other.width; + + if (data.bits != other.data.bits) + return data.bits < other.data.bits; + + return false; +} + +bool RTLIL::SigChunk::operator ==(const RTLIL::SigChunk &other) const +{ + if (wire != other.wire || width != other.width || offset != other.offset) + return false; + if (data.bits != other.data.bits) + return false; + return true; +} + +bool RTLIL::SigChunk::operator !=(const RTLIL::SigChunk &other) const +{ + if (*this == other) + return false; + return true; +} + +RTLIL::SigSpec::SigSpec() +{ + width = 0; +} + +RTLIL::SigSpec::SigSpec(const RTLIL::Const &data) +{ + chunks.push_back(RTLIL::SigChunk(data)); + width = chunks.back().width; + check(); +} + +RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk) +{ + chunks.push_back(chunk); + width = chunks.back().width; + check(); +} + +RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire, int width, int offset) +{ + chunks.push_back(RTLIL::SigChunk(wire, width, offset)); + this->width = chunks.back().width; + check(); +} + +RTLIL::SigSpec::SigSpec(const std::string &str) +{ + chunks.push_back(RTLIL::SigChunk(str)); + width = chunks.back().width; + check(); +} + +RTLIL::SigSpec::SigSpec(int val, int width) +{ + chunks.push_back(RTLIL::SigChunk(val, width)); + this->width = chunks.back().width; + check(); +} + +RTLIL::SigSpec::SigSpec(RTLIL::State bit, int width) +{ + chunks.push_back(RTLIL::SigChunk(bit, width)); + this->width = chunks.back().width; + check(); +} + +void RTLIL::SigSpec::expand() +{ + std::vector new_chunks; + for (size_t i = 0; i < chunks.size(); i++) { + assert(chunks[i].data.str.empty()); + for (int j = 0; j < chunks[i].width; j++) + new_chunks.push_back(chunks[i].extract(j, 1)); + } + chunks.swap(new_chunks); + check(); +} + +void RTLIL::SigSpec::optimize() +{ + for (size_t i = 0; i < chunks.size(); i++) { + if (chunks[i].wire && chunks[i].wire->auto_width) + continue; + if (chunks[i].width == 0) + chunks.erase(chunks.begin()+i--); + } + for (size_t i = 1; i < chunks.size(); i++) { + RTLIL::SigChunk &ch1 = chunks[i-1]; + RTLIL::SigChunk &ch2 = chunks[i]; + if (ch1.wire && ch1.wire->auto_width) + continue; + if (ch2.wire && ch2.wire->auto_width) + continue; + if (ch1.wire == ch2.wire) { + if (ch1.wire != NULL && ch1.offset+ch1.width == ch2.offset) { + ch1.width += ch2.width; + goto merged_with_next_chunk; + } + if (ch1.wire == NULL && ch1.data.str.empty() == ch2.data.str.empty()) { + ch1.data.str = ch2.data.str + ch1.data.str; + ch1.data.bits.insert(ch1.data.bits.end(), ch2.data.bits.begin(), ch2.data.bits.end()); + ch1.width += ch2.width; + goto merged_with_next_chunk; + } + } + if (0) { + merged_with_next_chunk: + chunks.erase(chunks.begin()+i); + i--; + } + } + check(); +} + +static bool compare_sigchunks(const RTLIL::SigChunk &a, const RTLIL::SigChunk &b) +{ + if (a.wire != b.wire) { + if (a.wire == NULL || b.wire == NULL) + return a.wire < b.wire; + else if (a.wire->name != b.wire->name) + return a.wire->name < b.wire->name; + else + return a.wire < b.wire; + } + if (a.offset != b.offset) + return a.offset < b.offset; + if (a.width != b.width) + return a.width < b.width; + return a.data.bits < b.data.bits; +} + +void RTLIL::SigSpec::sort_and_unify() +{ + expand(); + std::sort(chunks.begin(), chunks.end(), compare_sigchunks); + for (size_t i = 1; i < chunks.size(); i++) { + RTLIL::SigChunk &ch1 = chunks[i-1]; + RTLIL::SigChunk &ch2 = chunks[i]; + if (!compare_sigchunks(ch1, ch2) && !compare_sigchunks(ch2, ch1)) { + chunks.erase(chunks.begin()+i); + width -= chunks[i].width; + i--; + } + } + optimize(); +} + +void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with) +{ + replace(pattern, with, this); +} + +void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const +{ + int pos = 0, restart_pos = 0; + assert(other == NULL || width == other->width); + for (size_t i = 0; i < chunks.size(); i++) { +restart: + const RTLIL::SigChunk &ch1 = chunks[i]; + if (chunks[i].wire != NULL && pos >= restart_pos) + for (size_t j = 0, poff = 0; j < pattern.chunks.size(); j++) { + const RTLIL::SigChunk &ch2 = pattern.chunks[j]; + assert(ch2.wire != NULL); + if (ch1.wire == ch2.wire) { + int lower = std::max(ch1.offset, ch2.offset); + int upper = std::min(ch1.offset + ch1.width, ch2.offset + ch2.width); + if (lower < upper) { + restart_pos = pos+upper-ch1.offset; + other->replace(pos+lower-ch1.offset, with.extract(poff+lower-ch2.offset, upper-lower)); + goto restart; + } + } + poff += ch2.width; + } + pos += chunks[i].width; + } + check(); +} + +void RTLIL::SigSpec::remove(const RTLIL::SigSpec &pattern) +{ + remove2(pattern, NULL); +} + +void RTLIL::SigSpec::remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const +{ + RTLIL::SigSpec tmp = *this; + tmp.remove2(pattern, other); +} + +void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) +{ + int pos = 0; + assert(other == NULL || width == other->width); + for (size_t i = 0; i < chunks.size(); i++) { +restart: + const RTLIL::SigChunk &ch1 = chunks[i]; + if (chunks[i].wire != NULL) + for (size_t j = 0; j < pattern.chunks.size(); j++) { + const RTLIL::SigChunk &ch2 = pattern.chunks[j]; + assert(ch2.wire != NULL); + if (ch1.wire == ch2.wire) { + int lower = std::max(ch1.offset, ch2.offset); + int upper = std::min(ch1.offset + ch1.width, ch2.offset + ch2.width); + if (lower < upper) { + if (other) + other->remove(pos+lower-ch1.offset, upper-lower); + remove(pos+lower-ch1.offset, upper-lower); + if (i == chunks.size()) + break; + goto restart; + } + } + } + pos += chunks[i].width; + } + check(); +} + +RTLIL::SigSpec RTLIL::SigSpec::extract(RTLIL::SigSpec pattern, RTLIL::SigSpec *other) const +{ + int pos = 0; + RTLIL::SigSpec ret; + pattern.sort_and_unify(); + assert(other == NULL || width == other->width); + for (size_t i = 0; i < chunks.size(); i++) { + const RTLIL::SigChunk &ch1 = chunks[i]; + if (chunks[i].wire != NULL) + for (size_t j = 0; j < pattern.chunks.size(); j++) { + RTLIL::SigChunk &ch2 = pattern.chunks[j]; + assert(ch2.wire != NULL); + if (ch1.wire == ch2.wire) { + int lower = std::max(ch1.offset, ch2.offset); + int upper = std::min(ch1.offset + ch1.width, ch2.offset + ch2.width); + if (lower < upper) { + if (other) + ret.append(other->extract(pos+lower-ch1.offset, upper-lower)); + else + ret.append(extract(pos+lower-ch1.offset, upper-lower)); + } + } + } + pos += chunks[i].width; + } + ret.check(); + return ret; +} + +void RTLIL::SigSpec::replace(int offset, const RTLIL::SigSpec &with) +{ + int pos = 0; + assert(offset >= 0); + assert(with.width >= 0); + assert(offset+with.width <= width); + remove(offset, with.width); + for (size_t i = 0; i < chunks.size(); i++) { + if (pos == offset) { + chunks.insert(chunks.begin()+i, with.chunks.begin(), with.chunks.end()); + width += with.width; + check(); + return; + } + pos += chunks[i].width; + } + assert(pos == offset); + chunks.insert(chunks.end(), with.chunks.begin(), with.chunks.end()); + width += with.width; + check(); +} + +void RTLIL::SigSpec::remove_const() +{ + for (size_t i = 0; i < chunks.size(); i++) { + if (chunks[i].wire != NULL) + continue; + width -= chunks[i].width; + chunks.erase(chunks.begin() + (i--)); + } + check(); +} + +void RTLIL::SigSpec::remove(int offset, int length) +{ + int pos = 0; + assert(offset >= 0); + assert(length >= 0); + assert(offset+length <= width); + for (size_t i = 0; i < chunks.size(); i++) { + int orig_width = chunks[i].width; + if (pos+chunks[i].width > offset && pos < offset+length) { + int off = offset - pos; + int len = length; + if (off < 0) { + len += off; + off = 0; + } + if (len > chunks[i].width-off) + len = chunks[i].width-off; + RTLIL::SigChunk lsb_chunk = chunks[i].extract(0, off); + RTLIL::SigChunk msb_chunk = chunks[i].extract(off+len, chunks[i].width-off-len); + if (lsb_chunk.width == 0 && msb_chunk.width == 0) { + chunks.erase(chunks.begin()+i); + i--; + } else if (lsb_chunk.width == 0 && msb_chunk.width != 0) { + chunks[i] = msb_chunk; + } else if (lsb_chunk.width != 0 && msb_chunk.width == 0) { + chunks[i] = lsb_chunk; + } else if (lsb_chunk.width != 0 && msb_chunk.width != 0) { + chunks[i] = lsb_chunk; + chunks.insert(chunks.begin()+i+1, msb_chunk); + i++; + } else + assert(0); + width -= len; + } + pos += orig_width; + } + check(); +} + +RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const +{ + int pos = 0; + RTLIL::SigSpec ret; + assert(offset >= 0); + assert(length >= 0); + assert(offset+length <= width); + for (size_t i = 0; i < chunks.size(); i++) { + if (pos+chunks[i].width > offset && pos < offset+length) { + int off = offset - pos; + int len = length; + if (off < 0) { + len += off; + off = 0; + } + if (len > chunks[i].width-off) + len = chunks[i].width-off; + ret.chunks.push_back(chunks[i].extract(off, len)); + ret.width += len; + offset += len; + length -= len; + } + pos += chunks[i].width; + } + assert(length == 0); + ret.check(); + return ret; +} + +void RTLIL::SigSpec::append(const RTLIL::SigSpec &signal) +{ + for (size_t i = 0; i < signal.chunks.size(); i++) { + chunks.push_back(signal.chunks[i]); + width += signal.chunks[i].width; + } + check(); +} + +bool RTLIL::SigSpec::combine(RTLIL::SigSpec signal, RTLIL::State freeState, bool override) +{ + bool no_collisions = true; + + assert(width == signal.width); + expand(); + signal.expand(); + + for (size_t i = 0; i < chunks.size(); i++) { + bool self_free = chunks[i].wire == NULL && chunks[i].data.bits[0] == freeState; + bool other_free = signal.chunks[i].wire == NULL && signal.chunks[i].data.bits[0] == freeState; + if (!self_free && !other_free) { + if (override) + chunks[i] = signal.chunks[i]; + else + chunks[i] = RTLIL::SigChunk(RTLIL::State::Sx, 1); + no_collisions = false; + } + if (self_free && !other_free) + chunks[i] = signal.chunks[i]; + } + + optimize(); + return no_collisions; +} + +void RTLIL::SigSpec::extend(int width, bool is_signed) +{ + if (this->width > width) + remove(width, this->width - width); + + if (this->width < width) { + RTLIL::SigSpec padding = this->width > 0 ? extract(this->width - 1, 1) : RTLIL::SigSpec(RTLIL::State::S0); + if (!is_signed && padding != RTLIL::SigSpec(RTLIL::State::Sx) && padding != RTLIL::SigSpec(RTLIL::State::Sz) && + padding != RTLIL::SigSpec(RTLIL::State::Sa) && padding != RTLIL::SigSpec(RTLIL::State::Sm)) + padding = RTLIL::SigSpec(RTLIL::State::S0); + while (this->width < width) + append(padding); + } + + optimize(); +} + +void RTLIL::SigSpec::check() const +{ + int w = 0; + for (size_t i = 0; i < chunks.size(); i++) { + const RTLIL::SigChunk chunk = chunks[i]; + if (chunk.wire == NULL) { + assert(chunk.offset == 0); + assert(chunk.data.bits.size() == (size_t)chunk.width); + assert(chunk.data.str.size() == 0 || chunk.data.str.size()*8 == chunk.data.bits.size()); + } else { + assert(chunk.offset >= 0); + assert(chunk.width >= 0); + assert(chunk.offset + chunk.width <= chunk.wire->width); + assert(chunk.data.bits.size() == 0); + assert(chunk.data.str.size() == 0); + } + w += chunk.width; + } + assert(w == width); +} + +bool RTLIL::SigSpec::operator <(const RTLIL::SigSpec &other) const +{ + if (width != other.width) + return width < other.width; + + RTLIL::SigSpec a = *this, b = other; + a.optimize(); + b.optimize(); + + if (a.chunks.size() != b.chunks.size()) + return a.chunks.size() < b.chunks.size(); + + for (size_t i = 0; i < a.chunks.size(); i++) + if (a.chunks[i] != b.chunks[i]) + return a.chunks[i] < b.chunks[i]; + + return false; +} + +bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const +{ + if (width != other.width) + return false; + + RTLIL::SigSpec a = *this, b = other; + a.optimize(); + b.optimize(); + + if (a.chunks.size() != b.chunks.size()) + return false; + + for (size_t i = 0; i < a.chunks.size(); i++) + if (a.chunks[i] != b.chunks[i]) + return false; + + return true; +} + +bool RTLIL::SigSpec::operator !=(const RTLIL::SigSpec &other) const +{ + if (*this == other) + return false; + return true; +} + +bool RTLIL::SigSpec::is_fully_const() const +{ + for (auto it = chunks.begin(); it != chunks.end(); it++) + if (it->width > 0 && it->wire != NULL) + return false; + return true; +} + +bool RTLIL::SigSpec::is_fully_def() const +{ + for (auto it = chunks.begin(); it != chunks.end(); it++) { + if (it->width > 0 && it->wire != NULL) + return false; + for (size_t i = 0; i < it->data.bits.size(); i++) + if (it->data.bits[i] != RTLIL::State::S0 && it->data.bits[i] != RTLIL::State::S1) + return false; + } + return true; +} + +bool RTLIL::SigSpec::is_fully_undef() const +{ + for (auto it = chunks.begin(); it != chunks.end(); it++) { + if (it->width > 0 && it->wire != NULL) + return false; + for (size_t i = 0; i < it->data.bits.size(); i++) + if (it->data.bits[i] != RTLIL::State::Sx && it->data.bits[i] != RTLIL::State::Sz) + return false; + } + return true; +} + +bool RTLIL::SigSpec::has_marked_bits() const +{ + for (auto it = chunks.begin(); it != chunks.end(); it++) + if (it->width > 0 && it->wire == NULL) { + for (size_t i = 0; i < it->data.bits.size(); i++) + if (it->data.bits[i] == RTLIL::State::Sm) + return true; + } + return false; +} + +bool RTLIL::SigSpec::as_bool() const +{ + assert(is_fully_const()); + SigSpec sig = *this; + sig.optimize(); + if (sig.width) + return sig.chunks[0].data.as_bool(); + return false; +} + +int RTLIL::SigSpec::as_int() const +{ + assert(is_fully_const()); + SigSpec sig = *this; + sig.optimize(); + if (sig.width) + return sig.chunks[0].data.as_int(); + return 0; +} + +std::string RTLIL::SigSpec::as_string() const +{ + std::string str; + for (size_t i = chunks.size(); i > 0; i--) { + const RTLIL::SigChunk &chunk = chunks[i-1]; + if (chunk.wire != NULL) + for (int j = 0; j < chunk.width; j++) + str += "?"; + else + str += chunk.data.as_string(); + } + return str; +} + +RTLIL::Const RTLIL::SigSpec::as_const() const +{ + assert(is_fully_const()); + SigSpec sig = *this; + sig.optimize(); + if (sig.width) + return sig.chunks[0].data; + return RTLIL::Const(); +} + +bool RTLIL::SigSpec::match(std::string pattern) const +{ + std::string str = as_string(); + assert(pattern.size() == str.size()); + + for (size_t i = 0; i < pattern.size(); i++) { + if (pattern[i] == ' ') + continue; + if (pattern[i] == '*') { + if (str[i] != 'z' && str[i] != 'x') + return false; + continue; + } + if (pattern[i] != str[i]) + return false; + } + + return true; +} + +RTLIL::CaseRule::~CaseRule() +{ + for (auto it = switches.begin(); it != switches.end(); it++) + delete *it; +} + +void RTLIL::CaseRule::optimize() +{ + for (auto it : switches) + it->optimize(); + for (auto &it : compare) + it.optimize(); + for (auto &it : actions) { + it.first.optimize(); + it.second.optimize(); + } +} + +RTLIL::SwitchRule::~SwitchRule() +{ + for (auto it = cases.begin(); it != cases.end(); it++) + delete *it; +} + +void RTLIL::SwitchRule::optimize() +{ + signal.optimize(); + for (auto it : cases) + it->optimize(); +} + +void RTLIL::SyncRule::optimize() +{ + signal.optimize(); + for (auto &it : actions) { + it.first.optimize(); + it.second.optimize(); + } +} + +RTLIL::Process::~Process() +{ + for (auto it = syncs.begin(); it != syncs.end(); it++) + delete *it; +} + +void RTLIL::Process::optimize() +{ + root_case.optimize(); + for (auto it : syncs) + it->optimize(); +} + diff --git a/kernel/rtlil.h b/kernel/rtlil.h new file mode 100644 index 000000000..1f45d1204 --- /dev/null +++ b/kernel/rtlil.h @@ -0,0 +1,341 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef RTLIL_H +#define RTLIL_H + +#include +#include +#include +#include +#include + +std::string stringf(const char *fmt, ...); + +namespace RTLIL +{ + enum State { + S0 = 0, + S1 = 1, + Sx = 2, // undefined value or conflict + Sz = 3, // high-impedance / not-connected + Sa = 4, // don't care (used only in cases) + Sm = 5 // marker (used internally by some passes) + }; + enum SyncType { + ST0 = 0, // level sensitive: 0 + ST1 = 1, // level sensitive: 1 + STp = 2, // edge sensitive: posedge + STn = 3, // edge sensitive: negedge + STe = 4, // edge sensitive: both edges + STa = 5 // always active + }; + + extern int autoidx; + + struct Const; + struct Selection; + struct Design; + struct Module; + struct Wire; + struct Memory; + struct Cell; + struct SigChunk; + struct SigSpec; + struct CaseRule; + struct SwitchRule; + struct SyncRule; + struct Process; + + typedef std::pair SigSig; + +#ifdef NDEBUG + typedef std::string IdString; +#else + struct IdString : public std::string { + IdString() { } + IdString(std::string str) : std::string(str) { + check(); + } + IdString(const char *s) : std::string(s) { + check(); + } + IdString &operator=(const std::string &str) { + std::string::operator=(str); + check(); + return *this; + } + IdString &operator=(const char *s) { + std::string::operator=(s); + check(); + return *this; + } + bool operator<(const IdString &rhs) { + check(), rhs.check(); + return std::string(*this) < std::string(rhs); + } + void check() const { + assert(empty() || (size() >= 2 && (at(0) == '$' || at(0) == '\\'))); + } + }; +#endif + + static IdString escape_id(std::string str) __attribute__((unused)); + static IdString escape_id(std::string str) { + if (str.size() > 0 && str[0] != '\\' && str[0] != '$') + return "\\" + str; + return str; + } + + static std::string unescape_id(std::string str) __attribute__((unused)); + static std::string unescape_id(std::string str) { + if (str.size() > 0 && str[0] == '\\') + return str.substr(1); + return str; + } + + static IdString new_id(std::string file, int line, std::string func) __attribute__((unused)); + static IdString new_id(std::string file, int line, std::string func) { + std::string str = "$auto$"; + size_t pos = file.find_last_of('/'); + str += pos != std::string::npos ? file.substr(pos+1) : file; + str += stringf(":%d:%s$%d", line, func.c_str(), autoidx++); + return str; + } + +#define NEW_ID \ + RTLIL::new_id(__FILE__, __LINE__, __FUNCTION__) + + // see calc.cc for the implementation of this functions + RTLIL::Const const_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_xor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_xnor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + + RTLIL::Const const_reduce_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_reduce_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_reduce_xor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_reduce_xnor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_reduce_bool (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + + RTLIL::Const const_logic_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_logic_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_logic_or (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + + RTLIL::Const const_shl (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_shr (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_sshl (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_sshr (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + + RTLIL::Const const_lt (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_le (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_eq (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_ne (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_ge (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_gt (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + + RTLIL::Const const_add (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_sub (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_mul (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_div (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_mod (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_pow (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + + RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); +}; + +struct RTLIL::Const { + std::string str; + std::vector bits; + Const(std::string str = std::string()); + Const(int val, int width = 32); + Const(RTLIL::State bit, int width = 1); + Const(std::vector bits) : bits(bits) { }; + bool operator <(const RTLIL::Const &other) const; + bool operator ==(const RTLIL::Const &other) const; + bool operator !=(const RTLIL::Const &other) const; + bool as_bool() const; + int as_int() const; + std::string as_string() const; +}; + +struct RTLIL::Selection { + bool full_selection; + std::set selected_modules; + std::map> selected_members; + Selection(bool full = true) : full_selection(full) { } + bool selected_module(RTLIL::IdString mod_name); + bool selected_whole_module(RTLIL::IdString mod_name); + bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name); + void optimize(RTLIL::Design *design); +}; + +struct RTLIL::Design { + std::map modules; + std::vector selection_stack; + std::map selection_vars; + std::string selected_active_module; + ~Design(); + void check(); + void optimize(); + bool selected_module(RTLIL::IdString mod_name); + bool selected_whole_module(RTLIL::IdString mod_name); + bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name); + template bool selected(T1 *module) { + return selected_module(module->name); + } + template bool selected(T1 *module, T2 *member) { + return selected_member(module->name, member->name); + } +}; + +struct RTLIL::Module { + RTLIL::IdString name; + std::map wires; + std::map memories; + std::map cells; + std::map processes; + std::vector connections; + std::map attributes; + virtual ~Module(); + virtual RTLIL::IdString derive(RTLIL::Design *design, std::map parameters); + virtual void update_auto_wires(std::map auto_sizes); + virtual size_t count_id(RTLIL::IdString id); + virtual void check(); + virtual void optimize(); + void add(RTLIL::Wire *wire); + void add(RTLIL::Cell *cell); +}; + +struct RTLIL::Wire { + RTLIL::IdString name; + int width, start_offset, port_id; + bool port_input, port_output, auto_width; + std::map attributes; + Wire(); +}; + +struct RTLIL::Memory { + RTLIL::IdString name; + int width, start_offset, size; + std::map attributes; + Memory(); +}; + +struct RTLIL::Cell { + RTLIL::IdString name; + RTLIL::IdString type; + std::map connections; + std::map attributes; + std::map parameters; + void optimize(); +}; + +struct RTLIL::SigChunk { + RTLIL::Wire *wire; + RTLIL::Const data; // only used if wire == NULL, LSB at index 0 + int width, offset; + SigChunk(); + SigChunk(const RTLIL::Const &data); + SigChunk(RTLIL::Wire *wire, int width, int offset); + SigChunk(const std::string &str); + SigChunk(int val, int width = 32); + SigChunk(RTLIL::State bit, int width = 1); + RTLIL::SigChunk extract(int offset, int length) const; + bool operator <(const RTLIL::SigChunk &other) const; + bool operator ==(const RTLIL::SigChunk &other) const; + bool operator !=(const RTLIL::SigChunk &other) const; +}; + +struct RTLIL::SigSpec { + std::vector chunks; // LSB at index 0 + int width; + SigSpec(); + SigSpec(const RTLIL::Const &data); + SigSpec(const RTLIL::SigChunk &chunk); + SigSpec(RTLIL::Wire *wire, int width = -1, int offset = 0); + SigSpec(const std::string &str); + SigSpec(int val, int width = 32); + SigSpec(RTLIL::State bit, int width = 1); + void expand(); + void optimize(); + void sort_and_unify(); + void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with); + void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const; + void remove(const RTLIL::SigSpec &pattern); + void remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const; + void remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other); + RTLIL::SigSpec extract(RTLIL::SigSpec pattern, RTLIL::SigSpec *other = NULL) const; + void replace(int offset, const RTLIL::SigSpec &with); + void remove_const(); + void remove(int offset, int length); + RTLIL::SigSpec extract(int offset, int length) const; + void append(const RTLIL::SigSpec &signal); + bool combine(RTLIL::SigSpec signal, RTLIL::State freeState = RTLIL::State::Sz, bool override = false); + void extend(int width, bool is_signed = false); + void check() const; + bool operator <(const RTLIL::SigSpec &other) const; + bool operator ==(const RTLIL::SigSpec &other) const; + bool operator !=(const RTLIL::SigSpec &other) const; + bool is_fully_const() const; + bool is_fully_def() const; + bool is_fully_undef() const; + bool has_marked_bits() const; + bool as_bool() const; + int as_int() const; + std::string as_string() const; + RTLIL::Const as_const() const; + bool match(std::string pattern) const; +}; + +struct RTLIL::CaseRule { + std::vector compare; + std::vector actions; + std::vector switches; + ~CaseRule(); + void optimize(); +}; + +struct RTLIL::SwitchRule { + RTLIL::SigSpec signal; + std::map attributes; + std::vector cases; + ~SwitchRule(); + void optimize(); +}; + +struct RTLIL::SyncRule { + RTLIL::SyncType type; + RTLIL::SigSpec signal; + std::vector actions; + void optimize(); +}; + +struct RTLIL::Process { + RTLIL::IdString name; + std::map attributes; + RTLIL::CaseRule root_case; + std::vector syncs; + ~Process(); + void optimize(); +}; + +#endif diff --git a/kernel/select.cc b/kernel/select.cc new file mode 100644 index 000000000..8a91f1b1a --- /dev/null +++ b/kernel/select.cc @@ -0,0 +1,476 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +static std::vector work_stack; + +static bool match_ids(RTLIL::IdString id, std::string pattern) +{ + if (!fnmatch(pattern.c_str(), id.c_str(), FNM_NOESCAPE)) + return true; + if (id.size() > 0 && id[0] == '\\' && !fnmatch(pattern.c_str(), id.substr(1).c_str(), FNM_NOESCAPE)) + return true; + return false; +} + +static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + if (lhs.full_selection) { + lhs.full_selection = false; + lhs.selected_modules.clear(); + lhs.selected_members.clear(); + return; + } + + if (lhs.selected_modules.size() == 0 && lhs.selected_members.size() == 0) { + lhs.full_selection = true; + return; + } + + RTLIL::Selection new_sel(false); + + for (auto &mod_it : design->modules) + { + if (lhs.selected_whole_module(mod_it.first)) + continue; + if (!lhs.selected_module(mod_it.first)) { + new_sel.selected_modules.insert(mod_it.first); + continue; + } + + RTLIL::Module *mod = mod_it.second; + for (auto &it : mod->wires) + if (!lhs.selected_member(mod_it.first, it.first)) + new_sel.selected_members[mod->name].insert(it.first); + for (auto &it : mod->memories) + if (!lhs.selected_member(mod_it.first, it.first)) + new_sel.selected_members[mod->name].insert(it.first); + for (auto &it : mod->cells) + if (!lhs.selected_member(mod_it.first, it.first)) + new_sel.selected_members[mod->name].insert(it.first); + for (auto &it : mod->processes) + if (!lhs.selected_member(mod_it.first, it.first)) + new_sel.selected_members[mod->name].insert(it.first); + } + + lhs.selected_modules.swap(new_sel.selected_modules); + lhs.selected_members.swap(new_sel.selected_members); +} + +static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) +{ + if (rhs.full_selection) { + lhs.full_selection = true; + lhs.selected_modules.clear(); + lhs.selected_members.clear(); + return; + } + + if (lhs.full_selection) + return; + + for (auto &it : rhs.selected_members) + for (auto &it2 : it.second) + lhs.selected_members[it.first].insert(it2); + + for (auto &it : rhs.selected_modules) { + lhs.selected_modules.insert(it); + lhs.selected_members.erase(it); + } +} + +static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) +{ + if (rhs.full_selection) { + lhs.full_selection = false; + lhs.selected_modules.clear(); + lhs.selected_members.clear(); + return; + } + + if (lhs.full_selection) { + if (!rhs.full_selection && rhs.selected_modules.size() == 0 && rhs.selected_members.size() == 0) + return; + lhs.full_selection = false; + for (auto &it : design->modules) + lhs.selected_modules.insert(it.first); + } + + for (auto &it : rhs.selected_modules) { + lhs.selected_modules.erase(it); + lhs.selected_members.erase(it); + } + + for (auto &it : rhs.selected_members) + { + if (design->modules.count(it.first) == 0) + continue; + + RTLIL::Module *mod = design->modules[it.first]; + + if (lhs.selected_modules.count(mod->name) > 0) + { + for (auto &it : mod->wires) + lhs.selected_members[mod->name].insert(it.first); + for (auto &it : mod->memories) + lhs.selected_members[mod->name].insert(it.first); + for (auto &it : mod->cells) + lhs.selected_members[mod->name].insert(it.first); + for (auto &it : mod->processes) + lhs.selected_members[mod->name].insert(it.first); + lhs.selected_modules.erase(mod->name); + } + + if (lhs.selected_members.count(mod->name) == 0) + continue; + + for (auto &it2 : it.second) + lhs.selected_members[mod->name].erase(it2); + } +} + +static void select_op_intersect(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) +{ + if (rhs.full_selection) + return; + + if (lhs.full_selection) { + lhs.full_selection = false; + for (auto &it : design->modules) + lhs.selected_modules.insert(it.first); + } + + std::vector del_list; + + for (auto &it : lhs.selected_modules) + if (rhs.selected_modules.count(it) == 0) { + if (rhs.selected_members.count(it) > 0) + for (auto &it2 : rhs.selected_members.at(it)) + lhs.selected_members[it].insert(it2); + del_list.push_back(it); + } + for (auto &it : del_list) + lhs.selected_modules.erase(it); + + del_list.clear(); + for (auto &it : lhs.selected_members) { + if (rhs.selected_modules.count(it.first) > 0) + continue; + if (rhs.selected_members.count(it.first) == 0) { + del_list.push_back(it.first); + continue; + } + std::vector del_list2; + for (auto &it2 : it.second) + if (rhs.selected_members.at(it.first).count(it2) == 0) + del_list2.push_back(it2); + for (auto &it2 : del_list2) + it.second.erase(it2); + if (it.second.size() == 0) + del_list.push_back(it.first); + } + for (auto &it : del_list) + lhs.selected_members.erase(it); +} + +static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &sel) +{ + if (design->selected_active_module.empty()) + return; + + if (sel.full_selection) { + sel.full_selection = false; + sel.selected_modules.clear(); + sel.selected_members.clear(); + sel.selected_modules.insert(design->selected_active_module); + return; + } + + std::vector del_list; + for (auto mod_name : sel.selected_modules) + if (mod_name != design->selected_active_module) + del_list.push_back(mod_name); + for (auto &it : sel.selected_members) + if (it.first != design->selected_active_module) + del_list.push_back(it.first); + for (auto mod_name : del_list) { + sel.selected_modules.erase(mod_name); + sel.selected_members.erase(mod_name); + } +} + +static void select_stmt(RTLIL::Design *design, std::string arg) +{ + std::string arg_mod, arg_memb; + + if (arg.size() == 0) + return; + + if (arg[0] == '#') { + if (arg == "#") { + if (design->selection_stack.size() > 0) + work_stack.push_back(design->selection_stack.back()); + } else + if (arg == "#n") { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on stack for operator #n.\n"); + select_op_neg(design, work_stack[work_stack.size()-1]); + } else + if (arg == "#u") { + if (work_stack.size() < 2) + log_cmd_error("Must have at least two elements on stack for operator #u.\n"); + select_op_union(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]); + work_stack.pop_back(); + } else + if (arg == "#d") { + if (work_stack.size() < 2) + log_cmd_error("Must have at least two elements on stack for operator #d.\n"); + select_op_diff(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]); + work_stack.pop_back(); + } else + if (arg == "#i") { + if (work_stack.size() < 2) + log_cmd_error("Must have at least two elements on stack for operator #i.\n"); + select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]); + work_stack.pop_back(); + } else + log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str()); + select_filter_active_mod(design, work_stack.back()); + return; + } + + if (!design->selected_active_module.empty()) { + arg_mod = design->selected_active_module; + arg_memb = arg; + } else { + size_t pos = arg.find('/'); + if (pos == std::string::npos) { + arg_mod = arg; + } else { + arg_mod = arg.substr(0, pos); + arg_memb = arg.substr(pos+1); + } + } + + work_stack.push_back(RTLIL::Selection()); + RTLIL::Selection &sel = work_stack.back(); + + if (arg == "*" && arg_mod == "*") { + select_filter_active_mod(design, work_stack.back()); + return; + } + + sel.full_selection = false; + for (auto &mod_it : design->modules) + { + if (!match_ids(mod_it.first, arg_mod)) + continue; + + if (arg_memb == "") { + sel.selected_modules.insert(mod_it.first); + continue; + } + + RTLIL::Module *mod = mod_it.second; + if (arg_memb.substr(0, 2) == "w:") { + for (auto &it : mod->wires) + if (match_ids(it.first, arg_memb.substr(2))) + sel.selected_members[mod->name].insert(it.first); + } else + if (arg_memb.substr(0, 2) == "m:") { + for (auto &it : mod->memories) + if (match_ids(it.first, arg_memb.substr(2))) + sel.selected_members[mod->name].insert(it.first); + } else + if (arg_memb.substr(0, 2) == "c:") { + for (auto &it : mod->cells) + if (match_ids(it.first, arg_memb.substr(2))) + sel.selected_members[mod->name].insert(it.first); + } else + if (arg_memb.substr(0, 2) == "t:") { + for (auto &it : mod->cells) + if (match_ids(it.second->type, arg_memb.substr(2))) + sel.selected_members[mod->name].insert(it.first); + } else + if (arg_memb.substr(0, 2) == "p:") { + for (auto &it : mod->processes) + if (match_ids(it.first, arg_memb.substr(2))) + sel.selected_members[mod->name].insert(it.first); + } else { + if (arg_memb.substr(0, 2) == "n:") + arg_memb = arg_memb.substr(2); + for (auto &it : mod->wires) + if (match_ids(it.first, arg_memb)) + sel.selected_members[mod->name].insert(it.first); + for (auto &it : mod->memories) + if (match_ids(it.first, arg_memb)) + sel.selected_members[mod->name].insert(it.first); + for (auto &it : mod->cells) + if (match_ids(it.first, arg_memb)) + sel.selected_members[mod->name].insert(it.first); + for (auto &it : mod->processes) + if (match_ids(it.first, arg_memb)) + sel.selected_members[mod->name].insert(it.first); + } + } + + select_filter_active_mod(design, work_stack.back()); +} + +struct SelectPass : public Pass { + SelectPass() : Pass("select") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + bool add_mode = false; + bool del_mode = false; + bool clear_mode = false; + bool list_mode = false; + bool got_module = false; + + work_stack.clear(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-add") { + add_mode = true; + continue; + } + if (arg == "-del") { + del_mode = true; + continue; + } + if (arg == "-clear") { + clear_mode = true; + continue; + } + if (arg == "-list") { + list_mode = true; + continue; + } + if (arg == "-module" && argidx+1 < args.size()) { + RTLIL::IdString mod_name = RTLIL::escape_id(args[++argidx]); + if (design->modules.count(mod_name) == 0) + log_cmd_error("No such module: %s\n", mod_name.c_str()); + design->selected_active_module = mod_name; + got_module = true; + continue; + } + if (arg.size() > 0 && arg[0] == '-') + log_cmd_error("Unkown option %s.\n", arg.c_str()); + select_stmt(design, arg); + } + + if (clear_mode && args.size() != 2) + log_cmd_error("Option -clear can not be combined with other options.\n"); + + if (add_mode && del_mode) + log_cmd_error("Options -add and -del can not be combined.\n"); + + if (list_mode && (add_mode || del_mode)) + log_cmd_error("Option -list can not be combined with -add or -del.\n"); + + if (work_stack.size() == 0 && got_module) { + RTLIL::Selection sel; + select_filter_active_mod(design, sel); + work_stack.push_back(sel); + } + + while (work_stack.size() > 1) { + select_op_union(design, work_stack.front(), work_stack.back()); + work_stack.pop_back(); + } + + assert(design->selection_stack.size() > 0); + + if (clear_mode) + { + design->selection_stack.back() = RTLIL::Selection(true); + design->selected_active_module = std::string(); + return; + } + + if (list_mode) + { + RTLIL::Selection *sel = &design->selection_stack.back(); + if (work_stack.size() > 0) + sel = &work_stack.back(); + sel->optimize(design); + for (auto mod_it : design->modules) + { + if (design->selected_whole_module(mod_it.first)) + log("%s\n", mod_it.first.c_str()); + if (design->selected_module(mod_it.first)) { + for (auto &it : mod_it.second->wires) + if (design->selected_member(mod_it.first, it.first)) + log("%s/%s\n", mod_it.first.c_str(), it.first.c_str()); + for (auto &it : mod_it.second->memories) + if (design->selected_member(mod_it.first, it.first)) + log("%s/%s\n", mod_it.first.c_str(), it.first.c_str()); + for (auto &it : mod_it.second->cells) + if (design->selected_member(mod_it.first, it.first)) + log("%s/%s\n", mod_it.first.c_str(), it.first.c_str()); + for (auto &it : mod_it.second->processes) + if (design->selected_member(mod_it.first, it.first)) + log("%s/%s\n", mod_it.first.c_str(), it.first.c_str()); + } + } + return; + } + + if (add_mode) + { + if (work_stack.size() == 0) + log_cmd_error("Nothing to add to selection.\n"); + select_op_union(design, design->selection_stack.back(), work_stack.back()); + design->selection_stack.back().optimize(design); + return; + } + + if (del_mode) + { + if (work_stack.size() == 0) + log_cmd_error("Nothing to delete from selection.\n"); + select_op_diff(design, design->selection_stack.back(), work_stack.back()); + design->selection_stack.back().optimize(design); + return; + } + + if (work_stack.size() == 0) { + RTLIL::Selection &sel = design->selection_stack.back(); + if (sel.full_selection) + log("*\n"); + for (auto &it : sel.selected_modules) + log("%s\n", it.c_str()); + for (auto &it : sel.selected_members) + for (auto &it2 : it.second) + log("%s/%s\n", it.first.c_str(), it2.c_str()); + return; + } + + design->selection_stack.back() = work_stack.back(); + design->selection_stack.back().optimize(design); + } +} SelectPass; + diff --git a/kernel/sha1.cpp b/kernel/sha1.cpp new file mode 100644 index 000000000..fb7bfed6f --- /dev/null +++ b/kernel/sha1.cpp @@ -0,0 +1,185 @@ +/* + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Contributors: + Gustav + Several members in the gamedev.se forum. + Gregory Petrosyan + */ + +#include "sha1.h" + +namespace sha1 +{ + namespace // local + { + // Rotate an integer value to left. + inline unsigned int rol(const unsigned int value, + const unsigned int steps) + { + return ((value << steps) | (value >> (32 - steps))); + } + + // Sets the first 16 integers in the buffert to zero. + // Used for clearing the W buffert. + inline void clearWBuffert(unsigned int* buffert) + { + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } + } + + void innerHash(unsigned int* result, unsigned int* w) + { + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + + #define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) + { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + + #undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; + } + } // namespace + + void calc(const void* src, const int bytelength, unsigned char* hash) + { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + const unsigned char* sarray = (const unsigned char*) src; + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + const int endOfFullBlocks = bytelength - 64; + int endCurrentBlock; + int currentBlock = 0; + + while (currentBlock <= endOfFullBlocks) + { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on little endian. + w[roundPos++] = (unsigned int) sarray[currentBlock + 3] + | (((unsigned int) sarray[currentBlock + 2]) << 8) + | (((unsigned int) sarray[currentBlock + 1]) << 16) + | (((unsigned int) sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + int lastBlockBytes = 0; + for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) + { + w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); + } + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) + { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) + { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; + } + } + + void toHexString(const unsigned char* hash, char* hexstring) + { + const char hexDigits[] = { "0123456789abcdef" }; + + for (int hashByte = 20; --hashByte >= 0;) + { + hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf]; + hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf]; + } + hexstring[40] = 0; + } +} // namespace sha1 diff --git a/kernel/sha1.h b/kernel/sha1.h new file mode 100644 index 000000000..540c156d1 --- /dev/null +++ b/kernel/sha1.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace sha1 +{ + + /** + @param src points to any kind of data to be hashed. + @param bytelength the number of bytes to hash from the src pointer. + @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in. + */ + void calc(const void* src, const int bytelength, unsigned char* hash); + + /** + @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function. + @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string. + */ + void toHexString(const unsigned char* hash, char* hexstring); + +} // namespace sha1 + +#endif // SHA1_DEFINED diff --git a/kernel/show.cc b/kernel/show.cc new file mode 100644 index 000000000..d7da62cd2 --- /dev/null +++ b/kernel/show.cc @@ -0,0 +1,343 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include +#include + +#undef CLUSTER_CELLS_AND_PORTBOXES + +struct ShowWorker +{ + CellTypes ct; + + std::vector dot_escape_store; + std::map dot_id2num_store; + std::map autonames; + int single_idx_count; + + struct net_conn { std::set in, out; }; + std::map net_conn_map; + + FILE *f; + RTLIL::Design *design; + RTLIL::Module *module; + int page_counter; + + const char *escape(std::string id, bool is_name = false) + { + if (id.size() == 0) + return ""; + + if (id[0] == '$' && is_name) { + if (autonames.count(id) == 0) { + autonames[id] = autonames.size() + 1; + log("Generated short name for internal identifier: _%d_ -> %s\n", autonames[id], id.c_str()); + } + id = stringf("_%d_", autonames[id]); + } + + if (id[0] == '\\') + id = id.substr(1); + + std::string str; + for (char ch : id) { + if (ch == '\\' || ch == '"') + str += "\\"; + str += ch; + } + + dot_escape_store.push_back(str); + return dot_escape_store.back().c_str(); + } + + int id2num(RTLIL::IdString id) + { + if (dot_id2num_store.count(id) > 0) + return dot_id2num_store[id]; + return dot_id2num_store[id] = dot_id2num_store.size() + 1; + } + + std::string gen_signode_simple(RTLIL::SigSpec sig, bool range_check = true) + { + sig.optimize(); + + if (sig.chunks.size() == 0) { + fprintf(f, "v%d [ label=\"\" ];\n", single_idx_count); + return stringf("v%d", single_idx_count++); + } + + if (sig.chunks.size() == 1) { + RTLIL::SigChunk &c = sig.chunks[0]; + if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) { + if (!range_check || c.wire->width == c.width) + return stringf("n%d", id2num(c.wire->name)); + } else { + fprintf(f, "v%d [ label=\"%s\" ];\n", single_idx_count, escape(log_signal(c), true)); + return stringf("v%d", single_idx_count++); + } + } + + return std::string(); + } + + std::string gen_portbox(std::string port, RTLIL::SigSpec sig, bool driver, std::string *node = NULL) + { + std::string code; + std::string net = gen_signode_simple(sig); + if (net.empty()) + { + std::string label_string; + sig.optimize(); + int pos = sig.width-1; + int idx = single_idx_count++; + for (int i = int(sig.chunks.size())-1; i >= 0; i--) { + RTLIL::SigChunk &c = sig.chunks[i]; + net = gen_signode_simple(c, false); + assert(!net.empty()); + if (driver) { + label_string += stringf(" %d:%d - %d:%d |", i, pos, pos-c.width+1, c.offset+c.width-1, c.offset); + net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); + } else { + label_string += stringf(" %d:%d - %d:%d |", i, c.offset+c.width-1, c.offset, pos, pos-c.width+1); + net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i)); + } + pos -= c.width; + } + if (label_string[label_string.size()-1] == '|') + label_string = label_string.substr(0, label_string.size()-1); + code += stringf("x%d [ shape=record, style=rounded, label=\"%s\" ];\n", idx, label_string.c_str()); + if (!port.empty()) { + if (driver) + code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", port.c_str(), idx); + else + code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", idx, port.c_str()); + } + if (node != NULL) + *node = stringf("x%d", idx); + } + else + { + if (!port.empty()) { + if (driver) + net_conn_map[net].in.insert(port); + else + net_conn_map[net].out.insert(port); + } + if (node != NULL) + *node = net; + } + return code; + } + + void handle_module() + { + single_idx_count = 0; + dot_escape_store.clear(); + dot_id2num_store.clear(); + net_conn_map.clear(); + + fprintf(f, "digraph \"%s\" {\n", escape(module->name)); + fprintf(f, "rankdir=\"LR\";\n"); + fprintf(f, "remincross=true;\n"); + + std::map wires_on_demand; + for (auto &it : module->wires) { + if (!design->selected_member(module->name, it.first)) + continue; + const char *shape = "diamond"; + if (it.second->port_input || it.second->port_output) + shape = "octagon"; + if (it.first[0] == '\\') + fprintf(f, "n%d [ shape=%s, label=\"%s\" ];\n", + id2num(it.first), shape, escape(it.first)); + else { + wires_on_demand[stringf("n%d", id2num(it.first))] = it.first; + } + } + + for (auto &it : module->cells) + { + if (!design->selected_member(module->name, it.first)) + continue; + + std::vector in_ports, out_ports; + + for (auto &conn : it.second->connections) { + if (ct.cell_input(it.second->type, conn.first)) + in_ports.push_back(conn.first); + else + out_ports.push_back(conn.first); + } + + std::string label_string = "{{"; + + for (auto &p : in_ports) + label_string += stringf(" %s|", id2num(p), escape(p)); + if (label_string[label_string.size()-1] == '|') + label_string = label_string.substr(0, label_string.size()-1); + + label_string += stringf("}|%s\\n%s|{", escape(it.first, true), escape(it.second->type)); + + for (auto &p : out_ports) + label_string += stringf(" %s|", id2num(p), escape(p)); + if (label_string[label_string.size()-1] == '|') + label_string = label_string.substr(0, label_string.size()-1); + + label_string += "}}"; + + std::string code; + for (auto &conn : it.second->connections) { + code += gen_portbox(stringf("c%d:p%d", id2num(it.first), id2num(conn.first)), + conn.second, !ct.cell_input(it.second->type, conn.first)); + } + +#ifdef CLUSTER_CELLS_AND_PORTBOXES + if (!code.empty()) + fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\" ];\n%s}\n", + id2num(it.first), id2num(it.first), label_string.c_str(), code.c_str()); + else +#endif + fprintf(f, "c%d [ shape=record, label=\"%s\" ];\n%s", + id2num(it.first), label_string.c_str(), code.c_str()); + } + + for (auto &conn : module->connections) + { + bool found_lhs_wire = false; + for (auto &c : conn.first.chunks) { + if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) + found_lhs_wire = true; + } + bool found_rhs_wire = false; + for (auto &c : conn.second.chunks) { + if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) + found_rhs_wire = true; + } + if (!found_lhs_wire || !found_rhs_wire) + continue; + + std::string code, left_node, right_node; + code += gen_portbox("", conn.second, false, &left_node); + code += gen_portbox("", conn.first, true, &right_node); + fprintf(f, "%s", code.c_str()); + + if (left_node[0] == 'x' && right_node[0] == 'x') + fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", left_node.c_str(), right_node.c_str()); + else if (left_node[0] == 'x') + net_conn_map[right_node].in.insert(left_node); + else if (right_node[0] == 'x') + net_conn_map[left_node].out.insert(right_node); + else { + net_conn_map[right_node].in.insert(stringf("x%d:e", single_idx_count)); + net_conn_map[left_node].out.insert(stringf("x%d:w", single_idx_count)); + fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++); + } + } + + for (auto &it : net_conn_map) + { + if (wires_on_demand.count(it.first) > 0) { + if (it.second.in.size() == 1 && it.second.out.size() == 1) { + fprintf(f, "%s:e -> %s:w;\n", it.second.in.begin()->c_str(), it.second.out.begin()->c_str()); + continue; + } + if (it.second.in.size() == 0 || it.second.out.size() == 0) + fprintf(f, "%s [ shape=diamond, label=\"%s\" ];\n", it.first.c_str(), escape(wires_on_demand[it.first], true)); + else + fprintf(f, "%s [ shape=point ];\n", it.first.c_str()); + } + for (auto &it2 : it.second.in) + fprintf(f, "%s:e -> %s:w;\n", it2.c_str(), it.first.c_str()); + for (auto &it2 : it.second.out) + fprintf(f, "%s:e -> %s:w;\n", it.first.c_str(), it2.c_str()); + } + + fprintf(f, "};\n"); + } + + ShowWorker(FILE *f, RTLIL::Design *design) : f(f), design(design) + { + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + design->optimize(); + page_counter = 0; + for (auto &mod_it : design->modules) + { + module = mod_it.second; + if (!design->selected_module(module->name)) + continue; + if (design->selected_whole_module(module->name)) + log("Dumping module %s to page %d.\n", module->name.c_str(), ++page_counter); + else + log("Dumping selected parts of module %s to page %d.\n", module->name.c_str(), ++page_counter); + handle_module(); + } + } +}; + +struct ShowPass : public Pass { + ShowPass() : Pass("show") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Generating Graphviz representation of design.\n"); + + std::string viewer_exe; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-viewer" && argidx+1 < args.size()) { + viewer_exe = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + log("Writing dot description to `yosys-show.dot'.\n"); + FILE *f = fopen("yosys-show.dot", "w"); + if (f == NULL) + log_cmd_error("Can't open dot file `yosys-show.dot' for writing.\n"); + ShowWorker worker(f, design); + fclose(f); + + if (worker.page_counter == 0) + log_cmd_error("Nothing there to show.\n"); + + std::string cmd = stringf("dot -Tps -o yosys-show.ps yosys-show.dot"); + log("Exec: %s\n", cmd.c_str()); + if (system(cmd.c_str()) != 0) + log_cmd_error("Shell command failed!\n"); + + if (!viewer_exe.empty()) { + cmd = stringf("%s yosys-show.ps &", viewer_exe.c_str()); + log("Exec: %s\n", cmd.c_str()); + if (system(cmd.c_str()) != 0) + log_cmd_error("Shell command failed!\n"); + } + } +} ShowPass; + diff --git a/kernel/sigtools.h b/kernel/sigtools.h new file mode 100644 index 000000000..e76fd6023 --- /dev/null +++ b/kernel/sigtools.h @@ -0,0 +1,415 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef SIGTOOLS_H +#define SIGTOOLS_H + +#include "kernel/rtlil.h" +#include "kernel/log.h" +#include +#include + +struct SigPool +{ + typedef std::pair bitDef_t; + std::set bits; + + void clear() + { + bits.clear(); + } + + void add(RTLIL::SigSpec sig) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + bits.insert(bit); + } + } + + void add(const SigPool &other) + { + for (auto &bit : other.bits) + bits.insert(bit); + } + + void del(RTLIL::SigSpec sig) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + bits.erase(bit); + } + } + + void del(const SigPool &other) + { + for (auto &bit : other.bits) + bits.insert(bit); + } + + void expand(RTLIL::SigSpec from, RTLIL::SigSpec to) + { + from.expand(); + to.expand(); + assert(from.chunks.size() == to.chunks.size()); + for (size_t i = 0; i < from.chunks.size(); i++) { + bitDef_t bit_from(from.chunks[i].wire, from.chunks[i].offset); + bitDef_t bit_to(to.chunks[i].wire, to.chunks[i].offset); + if (bit_from.first == NULL || bit_to.first == NULL) + continue; + if (bits.count(bit_from) > 0) + bits.insert(bit_to); + } + } + + RTLIL::SigSpec extract(RTLIL::SigSpec sig) + { + RTLIL::SigSpec result; + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + bitDef_t bit(c.wire, c.offset); + if (bits.count(bit) > 0) + result.append(c); + } + return result; + } + + RTLIL::SigSpec remove(RTLIL::SigSpec sig) + { + RTLIL::SigSpec result; + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + bitDef_t bit(c.wire, c.offset); + if (bits.count(bit) == 0) + result.append(c); + } + return result; + } + + bool check_any(RTLIL::SigSpec sig) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + bitDef_t bit(c.wire, c.offset); + if (bits.count(bit) != 0) + return true; + } + return false; + } + + bool check_all(RTLIL::SigSpec sig) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + bitDef_t bit(c.wire, c.offset); + if (bits.count(bit) == 0) + return false; + } + return true; + } +}; + +template +struct SigSet +{ + typedef std::pair bitDef_t; + std::map> bits; + + void clear() + { + bits.clear(); + } + + void insert(RTLIL::SigSpec sig, T data) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + bits[bit].insert(data); + } + } + + void erase(RTLIL::SigSpec sig) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + bits[bit].clear(); + } + } + + void erase(RTLIL::SigSpec sig, T data) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + bits[bit].erase(data); + } + } + + void find(RTLIL::SigSpec sig, std::set &result) + { + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire == NULL) + continue; + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + for (auto &data : bits[bit]) + result.insert(data); + } + } + + std::set find(RTLIL::SigSpec sig) + { + std::set result; + find(sig, result); + return result; + } +}; + +struct SigMap +{ + typedef std::pair bitDef_t; + + struct shared_bit_data_t { + RTLIL::SigChunk chunk; + std::set bits; + }; + + std::map bits; + + SigMap(RTLIL::Module *module = NULL) + { + if (module != NULL) + set(module); + } + + SigMap(const SigMap &other) + { + copy(other); + } + + const SigMap &operator=(const SigMap &other) + { + copy(other); + return *this; + } + + void copy(const SigMap &other) + { + clear(); + for (auto &bit : other.bits) { + bits[bit.first] = new shared_bit_data_t; + bits[bit.first]->chunk = bit.second->chunk; + bits[bit.first]->bits = bit.second->bits; + } + } + + void swap(SigMap &other) + { + bits.swap(other.bits); + } + + ~SigMap() + { + clear(); + } + + void clear() + { + std::set all_bd_ptr; + for (auto &it : bits) + all_bd_ptr.insert(it.second); + for (auto bd_ptr : all_bd_ptr) + delete bd_ptr; + bits.clear(); + } + + void set(RTLIL::Module *module) + { + clear(); + for (auto &it : module->connections) + add(it.first, it.second); + } + + // internal helper function + void register_bit(const RTLIL::SigChunk &c) + { + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + if (c.wire && bits.count(bit) == 0) { + shared_bit_data_t *bd = new shared_bit_data_t; + bd->chunk = c; + bd->bits.insert(bit); + bits[bit] = bd; + } + } + + // internal helper function + void unregister_bit(const RTLIL::SigChunk &c) + { + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + if (c.wire && bits.count(bit) > 0) { + shared_bit_data_t *bd = bits[bit]; + bd->bits.erase(bit); + if (bd->bits.size() == 0) + delete bd; + bits.erase(bit); + } + } + + // internal helper function + void merge_bit(const RTLIL::SigChunk &c1, const RTLIL::SigChunk &c2) + { + assert(c1.wire != NULL && c2.wire != NULL); + assert(c1.width == 1 && c2.width == 1); + + bitDef_t b1(c1.wire, c1.offset); + bitDef_t b2(c2.wire, c2.offset); + + shared_bit_data_t *bd1 = bits[b1]; + shared_bit_data_t *bd2 = bits[b2]; + assert(bd1 != NULL && bd2 != NULL); + + if (bd1 == bd2) + return; + + if (bd1->bits.size() < bd2->bits.size()) + { + for (auto &bit : bd1->bits) + bits[bit] = bd2; + bd2->bits.insert(bd1->bits.begin(), bd1->bits.end()); + delete bd1; + } + else + { + bd1->chunk = bd2->chunk; + for (auto &bit : bd2->bits) + bits[bit] = bd1; + bd1->bits.insert(bd2->bits.begin(), bd2->bits.end()); + delete bd2; + } + } + + // internal helper function + void set_bit(const RTLIL::SigChunk &c1, const RTLIL::SigChunk &c2) + { + assert(c1.wire != NULL); + assert(c1.width == 1 && c2.width == 1); + bitDef_t bit(c1.wire, c1.offset); + assert(bits.count(bit) > 0); + bits[bit]->chunk = c2; + } + + // internal helper function + void map_bit(RTLIL::SigChunk &c) + { + assert(c.width == 1); + bitDef_t bit(c.wire, c.offset); + if (c.wire && bits.count(bit) > 0) + c = bits[bit]->chunk; + } + + void add(RTLIL::SigSpec from, RTLIL::SigSpec to) + { + from.expand(); + to.expand(); + + assert(from.chunks.size() == to.chunks.size()); + for (size_t i = 0; i < from.chunks.size(); i++) + { + RTLIL::SigChunk &cf = from.chunks[i]; + RTLIL::SigChunk &ct = to.chunks[i]; + + if (cf.wire == NULL) + continue; + + register_bit(cf); + register_bit(ct); + + if (ct.wire != NULL) + merge_bit(cf, ct); + else + set_bit(cf, ct); + } + } + + void add(RTLIL::SigSpec sig) + { + sig.expand(); + for (size_t i = 0; i < sig.chunks.size(); i++) + { + RTLIL::SigChunk &c = sig.chunks[i]; + if (c.wire != NULL) { + register_bit(c); + set_bit(c, c); + } + } + } + + void del(RTLIL::SigSpec sig) + { + sig.expand(); + for (auto &c : sig.chunks) + unregister_bit(c); + } + + void apply(RTLIL::SigSpec &sig) + { + sig.expand(); + for (auto &c : sig.chunks) + map_bit(c); + sig.optimize(); + } + + RTLIL::SigSpec operator()(RTLIL::SigSpec sig) + { + apply(sig); + return sig; + } +}; + +#endif /* SIGTOOLS_H */ diff --git a/passes/abc/Makefile.inc b/passes/abc/Makefile.inc new file mode 100644 index 000000000..25aadcdc0 --- /dev/null +++ b/passes/abc/Makefile.inc @@ -0,0 +1,4 @@ + +OBJS += passes/abc/abc.o +OBJS += passes/abc/vlparse.o + diff --git a/passes/abc/abc.cc b/passes/abc/abc.cc new file mode 100644 index 000000000..251d0ba09 --- /dev/null +++ b/passes/abc/abc.cc @@ -0,0 +1,645 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include "vlparse.h" +#include +#include +#include +#include +#include +#include +#include + +struct gate_t +{ + int id; + char type; + int in1, in2, in3; + bool is_port; + RTLIL::SigSpec sig; +}; + +static int map_autoidx; +static SigMap assign_map; +static RTLIL::Module *module; +static std::vector signal_list; +static std::map signal_map; + +static int map_signal(RTLIL::SigSpec sig, char gate_type = -1, int in1 = -1, int in2 = -1, int in3 = -1) +{ + assert(sig.width == 1); + assert(sig.chunks.size() == 1); + + assign_map.apply(sig); + + if (signal_map.count(sig) == 0) { + gate_t gate; + gate.id = signal_list.size(); + gate.type = -1; + gate.in1 = -1; + gate.in2 = -1; + gate.in3 = -1; + gate.is_port = false; + gate.sig = sig; + signal_list.push_back(gate); + signal_map[sig] = gate.id; + } + + gate_t &gate = signal_list[signal_map[sig]]; + + if (gate_type >= 0) + gate.type = gate_type; + if (in1 >= 0) + gate.in1 = in1; + if (in2 >= 0) + gate.in2 = in2; + if (in3 >= 0) + gate.in3 = in3; + + return gate.id; +} + +static void mark_port(RTLIL::SigSpec sig) +{ + assign_map.apply(sig); + sig.expand(); + for (auto &c : sig.chunks) { + if (c.wire != NULL && signal_map.count(c) > 0) + signal_list[signal_map[c]].is_port = true; + } +} + +static void extract_cell(RTLIL::Cell *cell) +{ + if (cell->type == "$_INV_") + { + RTLIL::SigSpec sig_a = cell->connections["\\A"]; + RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + + assign_map.apply(sig_a); + assign_map.apply(sig_y); + + map_signal(sig_y, 'n', map_signal(sig_a)); + + module->cells.erase(cell->name); + delete cell; + return; + } + + if (cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") + { + RTLIL::SigSpec sig_a = cell->connections["\\A"]; + RTLIL::SigSpec sig_b = cell->connections["\\B"]; + RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_y); + + if (cell->type == "$_AND_") + map_signal(sig_y, 'a', map_signal(sig_a), map_signal(sig_b)); + else if (cell->type == "$_OR_") + map_signal(sig_y, 'o', map_signal(sig_a), map_signal(sig_b)); + else if (cell->type == "$_XOR_") + map_signal(sig_y, 'x', map_signal(sig_a), map_signal(sig_b)); + else + abort(); + + module->cells.erase(cell->name); + delete cell; + return; + } + + if (cell->type == "$_MUX_") + { + RTLIL::SigSpec sig_a = cell->connections["\\A"]; + RTLIL::SigSpec sig_b = cell->connections["\\B"]; + RTLIL::SigSpec sig_s = cell->connections["\\S"]; + RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_s); + assign_map.apply(sig_y); + + map_signal(sig_y, 'm', map_signal(sig_a), map_signal(sig_b), map_signal(sig_s)); + + module->cells.erase(cell->name); + delete cell; + return; + } +} + +static std::string remap_name(std::string abc_name) +{ + std::stringstream sstr; + sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); + return sstr.str(); +} + +static void dump_loop_graph(FILE *f, int &nr, std::map> &edges, std::set &workpool, std::vector &in_counts) +{ + if (f == NULL) + return; + + log("Dumping loop state graph to slide %d.\n", ++nr); + + fprintf(f, "digraph slide%d {\n", nr); + fprintf(f, " rankdir=\"LR\";\n"); + + std::set nodes; + for (auto &e : edges) { + nodes.insert(e.first); + for (auto n : e.second) + nodes.insert(n); + } + + for (auto n : nodes) + fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].sig), + n, in_counts[n], workpool.count(n) ? ", shape=box" : ""); + + for (auto &e : edges) + for (auto n : e.second) + fprintf(f, " n%d -> n%d;\n", e.first, n); + + fprintf(f, "}\n"); +} + +static void handle_loops() +{ + // http://en.wikipedia.org/wiki/Topological_sorting + + std::map> edges; + std::vector in_edges_count(signal_list.size()); + std::set workpool; + + FILE *dot_f = NULL; + int dot_nr = 0; + + // uncomment for troubleshooting the loop detection code + // dot_f = fopen("test.dot", "w"); + + for (auto &g : signal_list) { + if (g.type == -1) { + workpool.insert(g.id); + } + if (g.in1 >= 0) { + edges[g.in1].insert(g.id); + in_edges_count[g.id]++; + } + if (g.in2 >= 0 && g.in2 != g.in1) { + edges[g.in2].insert(g.id); + in_edges_count[g.id]++; + } + if (g.in3 >= 0 && g.in3 != g.in2 && g.in3 != g.in1) { + edges[g.in3].insert(g.id); + in_edges_count[g.id]++; + } + } + + dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); + + while (workpool.size() > 0) + { + int id = *workpool.begin(); + workpool.erase(id); + + // log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].sig)); + + for (int id2 : edges[id]) { + assert(in_edges_count[id2] > 0); + if (--in_edges_count[id2] == 0) + workpool.insert(id2); + } + edges.erase(id); + + dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); + + while (workpool.size() == 0) + { + if (edges.size() == 0) + break; + + int id1 = edges.begin()->first; + + for (auto &edge_it : edges) { + int id2 = edge_it.first; + RTLIL::Wire *w1 = signal_list[id1].sig.chunks[0].wire; + RTLIL::Wire *w2 = signal_list[id2].sig.chunks[0].wire; + if (w1 != NULL) + continue; + else if (w2 == NULL) + id1 = id2; + else if (w1->name[0] == '$' && w2->name[0] == '\\') + id1 = id2; + else if (w1->name[0] == '\\' && w2->name[0] == '$') + continue; + else if (edges[id1].size() < edges[id2].size()) + id1 = id2; + else if (edges[id1].size() > edges[id2].size()) + continue; + else if (w1->name > w2->name) + id1 = id2; + } + + if (edges[id1].size() == 0) { + edges.erase(id1); + continue; + } + + RTLIL::Wire *wire = new RTLIL::Wire; + std::stringstream sstr; + sstr << "$abcloop$" << (RTLIL::autoidx++); + wire->name = sstr.str(); + module->wires[wire->name] = wire; + + bool first_line = true; + for (int id2 : edges[id1]) { + if (first_line) + log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)), + log_signal(signal_list[id1].sig), log_signal(signal_list[id2].sig)); + else + log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "", + log_signal(signal_list[id1].sig), log_signal(signal_list[id2].sig)); + first_line = false; + } + + int id3 = map_signal(RTLIL::SigSpec(wire)); + signal_list[id1].is_port = true; + signal_list[id3].is_port = true; + assert(id3 == int(in_edges_count.size())); + in_edges_count.push_back(0); + workpool.insert(id3); + + for (int id2 : edges[id1]) { + if (signal_list[id2].in1 == id1) + signal_list[id2].in1 = id3; + if (signal_list[id2].in2 == id1) + signal_list[id2].in2 = id3; + if (signal_list[id2].in3 == id1) + signal_list[id2].in3 = id3; + } + edges[id1].swap(edges[id3]); + + module->connections.push_back(RTLIL::SigSig(signal_list[id3].sig, signal_list[id1].sig)); + dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); + } + } + + if (dot_f != NULL) + fclose(dot_f); +} + +static void abc_module(RTLIL::Module *current_module, std::string script_file, std::string exe_file, std::string liberty_file, bool cleanup) +{ + module = current_module; + map_autoidx = RTLIL::autoidx++; + + signal_map.clear(); + signal_list.clear(); + assign_map.set(module); + + char tempdir_name[] = "/tmp/yosys-abc-XXXXXX"; + if (!cleanup) + tempdir_name[0] = tempdir_name[4] = '_'; + char *p = mkdtemp(tempdir_name); + log_header("Extracting gate logic of module `%s' to `%s/input.v'..\n", module->name.c_str(), tempdir_name); + assert(p != NULL); + + std::vector cells; + cells.reserve(module->cells.size()); + for (auto &it : module->cells) + cells.push_back(it.second); + for (auto c : cells) + extract_cell(c); + + for (auto &wire_it : module->wires) { + if (wire_it.second->port_id > 0) + mark_port(RTLIL::SigSpec(wire_it.second)); + } + + for (auto &cell_it : module->cells) + for (auto &port_it : cell_it.second->connections) + mark_port(port_it.second); + + handle_loops(); + + if (asprintf(&p, "%s/input.v", tempdir_name) < 0) abort(); + FILE *f = fopen(p, "wt"); + assert(f != NULL); + free(p); + + fprintf(f, "module logic ("); + bool first = true; + for (auto &si : signal_list) { + if (!si.is_port) + continue; + if (!first) + fprintf(f, ", "); + fprintf(f, "n%d", si.id); + first = false; + } + fprintf(f, "); // %s\n", module->name.c_str()); + + int count_input = 0, count_output = 0; + for (auto &si : signal_list) { + if (si.is_port) { + if (si.type >= 0) + count_output++; + else + count_input++; + } + fprintf(f, "%s n%d; // %s\n", si.is_port ? si.type >= 0 ? + "output" : "input" : "wire", si.id, log_signal(si.sig)); + } + for (auto &si : signal_list) { + assert(si.sig.width == 1 && si.sig.chunks.size() == 1); + if (si.sig.chunks[0].wire == NULL) + fprintf(f, "assign n%d = %c;\n", si.id, si.sig.chunks[0].data.bits[0] == RTLIL::State::S1 ? '1' : '0'); + } + + int count_gates = 0; + for (auto &si : signal_list) { + if (si.type == 'n') + fprintf(f, "not (n%d, n%d);\n", si.id, si.in1); + else if (si.type == 'a') + fprintf(f, "and (n%d, n%d, n%d);\n", si.id, si.in1, si.in2); + else if (si.type == 'o') + fprintf(f, "or (n%d, n%d, n%d);\n", si.id, si.in1, si.in2); + else if (si.type == 'x') + fprintf(f, "xor (n%d, n%d, n%d);\n", si.id, si.in1, si.in2); + else if (si.type == 'm') + fprintf(f, "assign n%d = n%d ? n%d : n%d;\n", si.id, si.in3, si.in2, si.in1); + else if (si.type >= 0) + abort(); + if (si.type >= 0) + count_gates++; + } + + fprintf(f, "endmodule\n"); + fclose(f); + + log("Extracted %d gates and %zd wires to a logic network with %d inputs and %d outputs.\n", + count_gates, signal_list.size(), count_input, count_output); + log_push(); + + if (count_output > 0) + { + log_header("Executing ABC.\n"); + + if (asprintf(&p, "%s/stdcells.genlib", tempdir_name) < 0) abort(); + f = fopen(p, "wt"); + assert(f != NULL); + fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); + fprintf(f, "GATE ONE 1 Y=CONST1;\n"); + fprintf(f, "GATE BUF 1 Y=A; PIN * NONINV 1 999 1 0 1 0\n"); + fprintf(f, "GATE INV 1 Y=!A; PIN * INV 1 999 1 0 1 0\n"); + fprintf(f, "GATE AND 1 Y=A*B; PIN * NONINV 1 999 1 0 1 0\n"); + fprintf(f, "GATE OR 1 Y=A+B; PIN * NONINV 1 999 1 0 1 0\n"); + fprintf(f, "GATE XOR 1 Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n"); + fprintf(f, "GATE MUX 1 Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n"); + fclose(f); + free(p); + + char buffer[1024]; + if (!liberty_file.empty()) + snprintf(buffer, 1024, "%s -c 'read_verilog %s/input.v; read_liberty %s; " + "map; write_verilog %s/output.v' 2>&1", exe_file.c_str(), tempdir_name, liberty_file.c_str(), tempdir_name); + else + if (!script_file.empty()) + snprintf(buffer, 1024, "%s -c 'read_verilog %s/input.v; source %s; " + "map; write_verilog %s/output.v' 2>&1", exe_file.c_str(), tempdir_name, script_file.c_str(), tempdir_name); + else + snprintf(buffer, 1024, "%s -c 'read_verilog %s/input.v; read_library %s/stdcells.genlib; " + "map; write_verilog %s/output.v' 2>&1", exe_file.c_str(), tempdir_name, tempdir_name, tempdir_name); + f = popen(buffer, "r"); + while (fgets(buffer, 1024, f) != NULL) + log("ABC: %s", buffer); + fclose(f); + + if (asprintf(&p, "%s/output.v", tempdir_name) < 0) abort(); + f = fopen(p, "rt"); + if (f == NULL) + log_error("Can't open ABC output file `%s'.\n", p); +#if 0 + RTLIL::Design *mapped_design = new RTLIL::Design; + frontend_register["verilog"]->execute(f, p, std::vector(), mapped_design); +#else + RTLIL::Design *mapped_design = abc_parse_verilog(f); +#endif + fclose(f); + free(p); + + log_header("Re-integrating ABC results.\n"); + RTLIL::Module *mapped_mod = mapped_design->modules["\\logic"]; + if (mapped_mod == NULL) + log_error("ABC output file does not contain a module `logic'.\n"); + for (auto &it : mapped_mod->wires) { + RTLIL::Wire *w = it.second; + RTLIL::Wire *wire = new RTLIL::Wire; + wire->name = remap_name(w->name); + module->wires[wire->name] = wire; + } + + std::map cell_stats; + if (liberty_file.empty() && script_file.empty()) + { + for (auto &it : mapped_mod->cells) { + RTLIL::Cell *c = it.second; + cell_stats[c->type.substr(1)]++; + if (c->type == "\\ZERO" || c->type == "\\ONE") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1); + module->connections.push_back(conn); + continue; + } + if (c->type == "\\BUF") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + conn.second = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); + module->connections.push_back(conn); + continue; + } + if (c->type == "\\INV") { + RTLIL::Cell *cell = new RTLIL::Cell; + cell->type = "$_INV_"; + cell->name = remap_name(c->name); + cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); + cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + module->cells[cell->name] = cell; + continue; + } + if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR") { + RTLIL::Cell *cell = new RTLIL::Cell; + cell->type = "$_" + c->type.substr(1) + "_"; + cell->name = remap_name(c->name); + cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); + cell->connections["\\B"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\B"].chunks[0].wire->name)]); + cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + module->cells[cell->name] = cell; + continue; + } + if (c->type == "\\MUX") { + RTLIL::Cell *cell = new RTLIL::Cell; + cell->type = "$_MUX_"; + cell->name = remap_name(c->name); + cell->connections["\\A"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\A"].chunks[0].wire->name)]); + cell->connections["\\B"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\B"].chunks[0].wire->name)]); + cell->connections["\\S"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\S"].chunks[0].wire->name)]); + cell->connections["\\Y"] = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + module->cells[cell->name] = cell; + continue; + } + assert(0); + } + } + else + { + for (auto &it : mapped_mod->cells) { + RTLIL::Cell *c = it.second; + cell_stats[c->type.substr(1)]++; + if (c->type == "$_const0_" || c->type == "$_const1_") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires[remap_name(c->connections["\\Y"].chunks[0].wire->name)]); + conn.second = RTLIL::SigSpec(c->type == "$_const0_" ? 0 : 1, 1); + module->connections.push_back(conn); + continue; + } + RTLIL::Cell *cell = new RTLIL::Cell; + cell->type = c->type; + cell->name = remap_name(c->name); + for (auto &conn : c->connections) + cell->connections[conn.first] = RTLIL::SigSpec(module->wires[remap_name(conn.second.chunks[0].wire->name)]); + module->cells[cell->name] = cell; + } + } + + for (auto &it : cell_stats) + log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); + int in_wires = 0, out_wires = 0; + for (auto &si : signal_list) + if (si.is_port) { + char buffer[100]; + snprintf(buffer, 100, "\\n%d", si.id); + RTLIL::SigSig conn; + if (si.type >= 0) { + conn.first = si.sig; + conn.second = RTLIL::SigSpec(module->wires[remap_name(buffer)]); + out_wires++; + } else { + conn.first = RTLIL::SigSpec(module->wires[remap_name(buffer)]); + conn.second = si.sig; + in_wires++; + } + module->connections.push_back(conn); + } + log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); + log("ABC RESULTS: input signals: %8d\n", in_wires); + log("ABC RESULTS: output signals: %8d\n", out_wires); + + delete mapped_design; + } + else + { + log("Don't call ABC as there is nothing to map.\n"); + } + + if (cleanup) + { + log_header("Removing temp directory `%s':\n", tempdir_name); + + struct dirent **namelist; + int n = scandir(tempdir_name, &namelist, 0, alphasort); + assert(n >= 0); + for (int i = 0; i < n; i++) { + if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) { + if (asprintf(&p, "%s/%s", tempdir_name, namelist[i]->d_name) < 0) abort(); + log("Removing `%s'.\n", p); + remove(p); + free(p); + } + free(namelist[i]); + } + free(namelist); + log("Removing `%s'.\n", tempdir_name); + rmdir(tempdir_name); + } + + log_pop(); +} + +struct AbcPass : public Pass { + AbcPass() : Pass("abc") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing ABC pass (technology mapping using ABC).\n"); + log_push(); + + std::string exe_file = "abc"; + std::string script_file, liberty_file; + bool cleanup = true; + + size_t argidx; + char *pwd = get_current_dir_name(); + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-exe" && argidx+1 < args.size()) { + exe_file = args[++argidx]; + continue; + } + if (arg == "-script" && argidx+1 < args.size() && liberty_file.empty()) { + script_file = args[++argidx]; + if (!script_file.empty() && script_file[0] != '/') + script_file = std::string(pwd) + "/" + script_file; + continue; + } + if (arg == "-liberty" && argidx+1 < args.size() && script_file.empty()) { + liberty_file = args[++argidx]; + if (!liberty_file.empty() && liberty_file[0] != '/') + liberty_file = std::string(pwd) + "/" + liberty_file; + continue; + } + if (arg == "-nocleanup") { + cleanup = false; + continue; + } + break; + } + free(pwd); + extra_args(args, argidx, design); + + for (auto &mod_it : design->modules) { + if (mod_it.second->processes.size() > 0) + log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str()); + else + abc_module(mod_it.second, script_file, exe_file, liberty_file, cleanup); + } + + assign_map.clear(); + signal_list.clear(); + signal_map.clear(); + + log_pop(); + } +} AbcPass; + diff --git a/passes/abc/vlparse.cc b/passes/abc/vlparse.cc new file mode 100644 index 000000000..5c0cb7e24 --- /dev/null +++ b/passes/abc/vlparse.cc @@ -0,0 +1,198 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "vlparse.h" +#include "kernel/log.h" +#include +#include + +static int lex_line, lex_tok; +static std::string lex_str; + +static int token(int tok) +{ + lex_tok = tok; +#if 0 + if (lex_tok == 256) + fprintf(stderr, "STR in line %d: >>%s<<\n", lex_line, lex_str.c_str()); + else if (tok >= 32 && tok < 255) + fprintf(stderr, "CHAR in line %d: >>%c<<\n", lex_line, lex_tok); + else + fprintf(stderr, "CHAR in line %d: %d\n", lex_line, lex_tok); +#endif + return tok; +} + +static int lex(FILE *f) +{ + int ch = getc(f); + + while (ch == ' ' || ch == '\t' || ch == '\n') { + if (ch == '\n') + lex_line++; + ch = getc(f); + } + + if (ch <= 0 || 255 < ch) + return token(lex_tok); + + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_') { + lex_str = char(ch); + while (1) { + ch = getc(f); + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_') { + lex_str += char(ch); + continue; + } + break; + } + ungetc(ch, f); + return token(256); + } + + if (ch == '/') { + ch = getc(f); + if (ch == '/') { + while (ch != '\n') + ch = getc(f); + ungetc(ch, f); + return lex(f); + } + ungetc(ch, f); + return token('/'); + } + + return token(ch); +} + +RTLIL::Design *abc_parse_verilog(FILE *f) +{ + RTLIL::Design *design = new RTLIL::Design; + RTLIL::Module *module; + RTLIL::Wire *wire; + RTLIL::Cell *cell; + + int port_count = 1; + lex_line = 1; + + // parse module header + if (lex(f) != 256 || lex_str != "module") + goto error; + if (lex(f) != 256) + goto error; + + module = new RTLIL::Module; + module->name = "\\" + lex_str; + design->modules[module->name] = module; + + if (lex(f) != '(') + goto error; + while (lex(f) != ')') { + if (lex_tok != 256 && lex_tok != ',') + goto error; + } + if (lex(f) != ';') + goto error; + + // parse module body + while (1) + { + if (lex(f) != 256) + goto error; + + if (lex_str == "endmodule") + return design; + + if (lex_str == "input" || lex_str == "output" || lex_str == "wire") + { + std::string mode = lex_str; + while (lex(f) != ';') { + if (lex_tok != 256 && lex_tok != ',') + goto error; + if (lex_tok == 256) { + // printf("%s [%s]\n", mode.c_str(), lex_str.c_str()); + wire = new RTLIL::Wire; + wire->name = "\\" + lex_str; + if (mode == "input") { + wire->port_id = port_count++; + wire->port_input = true; + } + if (mode == "output") { + wire->port_id = port_count++; + wire->port_output = true; + } + module->wires[wire->name] = wire; + } + } + } + else + { + std::string cell_type = lex_str; + + if (lex(f) != 256) + goto error; + + std::string cell_name = lex_str; + + if (lex(f) != '(') + goto error; + + // printf("cell [%s] [%s]\n", cell_type.c_str(), cell_name.c_str()); + cell = new RTLIL::Cell; + cell->type = "\\" + cell_type; + cell->name = "\\" + cell_name; + module->cells[cell->name] = cell; + + lex(f); + while (lex_tok != ')') + { + if (lex_tok != '.' || lex(f) != 256) + goto error; + + std::string cell_port = lex_str; + + if (lex(f) != '(' || lex(f) != 256) + goto error; + + std::string wire_name = lex_str; + + // printf(" [%s] <- [%s]\n", cell_port.c_str(), wire_name.c_str()); + if (module->wires.count("\\" + wire_name) == 0) + goto error; + cell->connections["\\" + cell_port] = RTLIL::SigSpec(module->wires["\\" + wire_name]); + + if (lex(f) != ')' || (lex(f) != ',' && lex_tok != ')')) + goto error; + while (lex_tok == ',') + lex(f); + } + + if (lex(f) != ';') + goto error; + } + } + +error: + log_error("Syntax error in line %d!\n", lex_line); + // delete design; + // return NULL; +} + diff --git a/passes/abc/vlparse.h b/passes/abc/vlparse.h new file mode 100644 index 000000000..9514c4190 --- /dev/null +++ b/passes/abc/vlparse.h @@ -0,0 +1,28 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef ABC_VLPARSE +#define ABC_VLPARSE + +#include "kernel/rtlil.h" + +extern RTLIL::Design *abc_parse_verilog(FILE *f); + +#endif + diff --git a/passes/dfflibmap/Makefile.inc b/passes/dfflibmap/Makefile.inc new file mode 100644 index 000000000..ed92b299c --- /dev/null +++ b/passes/dfflibmap/Makefile.inc @@ -0,0 +1,10 @@ + +OBJS += passes/dfflibmap/dfflibmap.o +OBJS += passes/dfflibmap/libparse.o + +TARGETS += filterlib +GENFILES += passes/dfflibmap/filterlib.o + +filterlib: passes/dfflibmap/filterlib.o + $(CXX) -o filterlib $(LDFLAGS) $^ $(LDLIBS) + diff --git a/passes/dfflibmap/dfflibmap.cc b/passes/dfflibmap/dfflibmap.cc new file mode 100644 index 000000000..86e8bcbf6 --- /dev/null +++ b/passes/dfflibmap/dfflibmap.cc @@ -0,0 +1,326 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include "libparse.h" +#include + +using namespace PASS_DFFLIBMAP; + +struct cell_mapping { + std::string cell_name; + std::map ports; +}; +static std::map cell_mappings; + +static void logmap(std::string dff) +{ + if (cell_mappings.count(dff) == 0) { + log(" unmapped dff cell: %s\n", dff.c_str()); + } else { + log(" %s %s(", cell_mappings[dff].cell_name.c_str(), dff.substr(1).c_str()); + bool first = true; + for (auto &port : cell_mappings[dff].ports) { + char arg[3] = { port.second, 0, 0 }; + if ('a' <= arg[0] && arg[0] <= 'z') + arg[1] = arg[0] - ('a' - 'A'), arg[0] = '~'; + log("%s.%s(%s)", first ? "" : ", ", port.first.c_str(), arg); + first = false; + } + log(");\n"); + } +} + +static void logmap_all() +{ + logmap("$_DFF_N_"); + logmap("$_DFF_P_"); + logmap("$_DFF_NN0_"); + logmap("$_DFF_NN1_"); + logmap("$_DFF_NP0_"); + logmap("$_DFF_NP1_"); + logmap("$_DFF_PN0_"); + logmap("$_DFF_PN1_"); + logmap("$_DFF_PP0_"); + logmap("$_DFF_PP1_"); +} + +static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, bool &pin_pol) +{ + if (cell == NULL || attr == NULL || attr->value.empty()) + return false; + + std::string value = attr->value; + + for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) + value.erase(pos, 1); + + if (value[value.size()-1] == '\'') { + pin_name = value.substr(0, value.size()-1); + pin_pol = false; + } else { + pin_name = value; + pin_pol = true; + } + + for (auto child : cell->children) + if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name) + return true; + return false; +} + +static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval) +{ + LibertyAst *best_cell = NULL; + std::map best_cell_ports; + int best_cell_pins = 0; + + if (ast->id != "library") + log_error("Format error in liberty file.\n"); + + for (auto cell : ast->children) + { + if (cell->id != "cell" || cell->args.size() != 1) + continue; + + LibertyAst *ff = cell->find("ff"); + if (ff == NULL) + continue; + + std::string cell_clk_pin, cell_rst_pin, cell_next_pin; + bool cell_clk_pol, cell_rst_pol, cell_next_pol; + + if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol) + continue; + if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol)) + continue; + if (has_reset && rstval == false) { + if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol) + continue; + } + if (has_reset && rstval == true) { + if (!parse_pin(cell, ff->find("preset"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol) + continue; + } + + std::map this_cell_ports; + this_cell_ports[cell_clk_pin] = 'C'; + if (has_reset) + this_cell_ports[cell_rst_pin] = 'R'; + this_cell_ports[cell_next_pin] = 'D'; + + int num_pins = 0; + bool found_output = false; + for (auto pin : cell->children) + { + if (pin->id != "pin" || pin->args.size() != 1) + continue; + + LibertyAst *dir = pin->find("direction"); + if (dir == NULL || dir->value == "internal") + continue; + num_pins++; + + if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0) + goto continue_cell_loop; + + LibertyAst *func = pin->find("function"); + if (dir->value == "output" && func != NULL) { + std::string value = func->value; + for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) + value.erase(pos, 1); + if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) { + this_cell_ports[pin->args[0]] = 'Q'; + found_output = true; + } + } + + if (this_cell_ports.count(pin->args[0]) == 0) + this_cell_ports[pin->args[0]] = 0; + } + + if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + continue; + + best_cell = cell; + best_cell_pins = num_pins; + best_cell_ports.swap(this_cell_ports); + continue_cell_loop:; + } + + if (best_cell != NULL) { + log(" cell %s is a direct match for cell type %s.\n", best_cell->args[0].c_str(), cell_type.substr(1).c_str()); + cell_mappings[cell_type].cell_name = best_cell->args[0]; + cell_mappings[cell_type].ports = best_cell_ports; + } +} + +static bool expand_cellmap_worker(std::string from, std::string to, std::string inv) +{ + if (cell_mappings.count(to) > 0) + return false; + + log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str()); + cell_mappings[to].cell_name = cell_mappings[from].cell_name; + cell_mappings[to].ports = cell_mappings[from].ports; + + for (auto &it : cell_mappings[to].ports) { + if (inv.find(it.second) == std::string::npos) + continue; + if ('a' <= it.second && it.second <= 'z') + it.second -= 'a' - 'A'; + else if ('A' <= it.second && it.second <= 'Z') + it.second += 'a' - 'A'; + } + return true; +} + +static bool expand_cellmap(std::string pattern, std::string inv) +{ + std::vector> from_to_list; + bool return_status = false; + + for (auto &it : cell_mappings) { + std::string from = it.first, to = it.first; + if (from.size() != pattern.size()) + continue; + for (size_t i = 0; i < from.size(); i++) { + if (pattern[i] == '*') { + to[i] = from[i] == 'P' ? 'N' : + from[i] == 'N' ? 'P' : + from[i] == '1' ? '0' : + from[i] == '0' ? '1' : '*'; + } else + if (pattern[i] != '?' && pattern[i] != from[i]) + goto pattern_failed; + } + from_to_list.push_back(std::pair(from, to)); + pattern_failed:; + } + + for (auto &it : from_to_list) + return_status = return_status || expand_cellmap_worker(it.first, it.second, inv); + return return_status; +} + +static void dfflibmap(RTLIL::Module *module) +{ + log("Mapping DFF cells in module `%s':\n", module->name.c_str()); + + std::vector cell_list; + for (auto &it : module->cells) { + if (cell_mappings.count(it.second->type) > 0) + cell_list.push_back(it.second); + } + + std::map stats; + for (auto cell : cell_list) { + cell_mapping &cm = cell_mappings[cell->type]; + RTLIL::Cell *new_cell = new RTLIL::Cell; + new_cell->name = cell->name; + new_cell->type = "\\" + cm.cell_name; + for (auto &port : cm.ports) { + RTLIL::SigSpec sig; + if ('A' <= port.second && port.second <= 'Z') { + sig = cell->connections[std::string("\\") + port.second]; + } else + if ('a' <= port.second && port.second <= 'z') { + sig = cell->connections[std::string("\\") + char(port.second - ('a' - 'A'))]; + RTLIL::Cell *inv_cell = new RTLIL::Cell; + RTLIL::Wire *inv_wire = new RTLIL::Wire; + inv_cell->name = stringf("$dfflibmap$inv$%d", RTLIL::autoidx); + inv_wire->name = stringf("$dfflibmap$sig$%d", RTLIL::autoidx++); + inv_cell->type = "$_INV_"; + inv_cell->connections[port.second == 'q' ? "\\Y" : "\\A"] = sig; + sig = RTLIL::SigSpec(inv_wire); + inv_cell->connections[port.second == 'q' ? "\\A" : "\\Y"] = sig; + module->cells[inv_cell->name] = inv_cell; + module->wires[inv_wire->name] = inv_wire; + } + new_cell->connections["\\" + port.first] = sig; + } + stats[stringf(" mapped %%d %s cells to %s cells.\n", cell->type.c_str(), new_cell->type.c_str())]++; + module->cells[cell->name] = new_cell; + delete cell; + } + + for (auto &stat: stats) + log(stat.first.c_str(), stat.second); +} + +struct DfflibmapPass : public Pass { + DfflibmapPass() : Pass("dfflibmap") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); + + std::string liberty_file; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-liberty" && argidx+1 < args.size()) { + liberty_file = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (liberty_file.empty()) + log_cmd_error("Missing `-liberty liberty_file' option!\n"); + + FILE *f = fopen(liberty_file.c_str(), "r"); + if (f == NULL) + log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); + LibertyParer libparser(f); + fclose(f); + + find_cell(libparser.ast, "$_DFF_N_", false, false, false, false); + find_cell(libparser.ast, "$_DFF_P_", true, false, false, false); + find_cell(libparser.ast, "$_DFF_NN0_", false, true, false, false); + find_cell(libparser.ast, "$_DFF_NN1_", false, true, false, true); + find_cell(libparser.ast, "$_DFF_NP0_", false, true, true, false); + find_cell(libparser.ast, "$_DFF_NP1_", false, true, true, true); + find_cell(libparser.ast, "$_DFF_PN0_", true, true, false, false); + find_cell(libparser.ast, "$_DFF_PN1_", true, true, false, true); + find_cell(libparser.ast, "$_DFF_PP0_", true, true, true, false); + find_cell(libparser.ast, "$_DFF_PP1_", true, true, true, true); + + bool keep_running = true; + while (keep_running) { + keep_running = false; + keep_running |= expand_cellmap("$_DFF_*_", "C"); + keep_running |= expand_cellmap("$_DFF_*??_", "C"); + keep_running |= expand_cellmap("$_DFF_?*?_", "R"); + keep_running |= expand_cellmap("$_DFF_??*_", "DQ"); + } + + log(" final dff cell mappings:\n"); + logmap_all(); + + for (auto &it : design->modules) + dfflibmap(it.second); + + cell_mappings.clear(); + } +} DfflibmapPass; + diff --git a/passes/dfflibmap/filterlib.cc b/passes/dfflibmap/filterlib.cc new file mode 100644 index 000000000..05cfa6d24 --- /dev/null +++ b/passes/dfflibmap/filterlib.cc @@ -0,0 +1,4 @@ + +#define FILTERLIB +#include "libparse.cc" + diff --git a/passes/dfflibmap/libparse.cc b/passes/dfflibmap/libparse.cc new file mode 100644 index 000000000..8fc03b5c9 --- /dev/null +++ b/passes/dfflibmap/libparse.cc @@ -0,0 +1,629 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "libparse.h" +#include +#include + +#ifndef FILTERLIB +#include "kernel/log.h" +#endif + +using namespace PASS_DFFLIBMAP; + +std::set LibertyAst::blacklist; +std::set LibertyAst::whitelist; + +LibertyAst::~LibertyAst() +{ + for (auto child : children) + delete child; + children.clear(); +} + +LibertyAst *LibertyAst::find(std::string name) +{ + if (this == NULL) + return NULL; + for (auto child : children) + if (child->id == name) + return child; + return NULL; +} + +void LibertyAst::dump(FILE *f, std::string indent, std::string path, bool path_ok) +{ + if (whitelist.count(path + "/*") > 0) + path_ok = true; + + path += "/" + id; + + if (blacklist.count(id) > 0 || blacklist.count(path) > 0) + return; + if (whitelist.size() > 0 && whitelist.count(id) == 0 && whitelist.count(path) == 0 && !path_ok) { + fprintf(stderr, "Automatically added to blacklist: %s\n", path.c_str()); + blacklist.insert(id); + return; + } + + fprintf(f, "%s%s", indent.c_str(), id.c_str()); + if (!args.empty()) { + for (size_t i = 0; i < args.size(); i++) + fprintf(f, "%s%s", i > 0 ? ", " : "(", args[i].c_str()); + fprintf(f, ")"); + } + if (!value.empty()) + fprintf(f, " : %s", value.c_str()); + if (!children.empty()) { + fprintf(f, " {\n"); + for (size_t i = 0; i < children.size(); i++) + children[i]->dump(f, indent + " ", path, path_ok); + fprintf(f, "%s}\n", indent.c_str()); + } else + fprintf(f, " ;\n"); +} + +int LibertyParer::lexer(std::string &str) +{ + int c; + + do { + c = fgetc(f); + } while (c == ' ' || c == '\t' || c == '\r'); + + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '.') { + str = c; + while (1) { + c = fgetc(f); + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '.') + str += c; + else + break; + } + ungetc(c, f); + // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); + return 'v'; + } + + if (c == '"') { + str = c; + while (1) { + c = fgetc(f); + if (c == '\n') + line++; + str += c; + if (c == '"') + break; + } + // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str()); + return 'v'; + } + + if (c == '/') { + c = fgetc(f); + if (c == '*') { + int last_c = 0; + while (c > 0 && (last_c != '*' || c != '/')) { + last_c = c; + c = fgetc(f); + if (c == '\n') + line++; + } + return lexer(str); + } + ungetc(c, f); + // fprintf(stderr, "LEX: char >>/<<\n"); + return '/'; + } + + if (c == '\\') { + c = fgetc(f); + if (c == '\r') + c = fgetc(f); + if (c == '\n') + return lexer(str); + ungetc(c, f); + return '\\'; + } + + if (c == '\n') { + line++; + return ';'; + } + + // if (c >= 32 && c < 255) + // fprintf(stderr, "LEX: char >>%c<<\n", c); + // else + // fprintf(stderr, "LEX: char %d\n", c); + return c; +} + +LibertyAst *LibertyParer::parse() +{ + std::string str; + + int tok = lexer(str); + + while (tok == ';') + tok = lexer(str); + + if (tok == '}' || tok < 0) + return NULL; + + if (tok != 'v') + error(); + + LibertyAst *ast = new LibertyAst; + ast->id = str; + + while (1) + { + tok = lexer(str); + + if (tok == ';') + break; + + if (tok == ':' && ast->value.empty()) { + tok = lexer(ast->value); + if (tok != 'v') + error(); + continue; + } + + if (tok == '(') { + while (1) { + std::string arg; + tok = lexer(arg); + if (tok == ',') + continue; + if (tok == ')') + break; + if (tok != 'v') + error(); + ast->args.push_back(arg); + } + continue; + } + + if (tok == '{') { + while (1) { + LibertyAst *child = parse(); + if (child == NULL) + break; + ast->children.push_back(child); + } + break; + } + + error(); + } + + return ast; +} + +#ifndef FILTERLIB + +void LibertyParer::error() +{ + log_error("Syntax error in line %d.\n", line); +} + +#else + +void LibertyParer::error() +{ + fprintf(stderr, "Syntax error in line %d.\n", line); + exit(1); +} + +/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/ + +// This is to not confuse the VIM syntax highlighting +#define CHECK_VAL_OPEN ( +#define CHECK_VAL_CLOSE ) + +#define CHECK(result, check) \ + CHECK_VAL_OPEN{ \ + auto _R = (result); \ + if (!(_R check)) { \ + fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \ + #result, (long int)_R, #check, __FILE__, __LINE__); \ + abort(); \ + } \ + _R; \ + }CHECK_VAL_CLOSE + +#define CHECK_NV(result, check) \ + do { \ + auto _R = (result); \ + if (!(_R check)) { \ + fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \ + #result, (long int)_R, #check, __FILE__, __LINE__); \ + abort(); \ + } \ + } while(0) + +#define CHECK_COND(result) \ + do { \ + if (!(result)) { \ + fprintf(stderr, "Error from '%s' in %s:%d.\n", \ + #result, __FILE__, __LINE__); \ + abort(); \ + } \ + } while(0) + +/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/ + +std::string func2vl(std::string str) +{ + for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) { + char c_left = pos > 0 ? str[pos-1] : ' '; + char c_right = pos+1 < str.size() ? str[pos+1] : ' '; + if (std::string("\" \t*+").find(c_left) != std::string::npos) + str.erase(pos, 1); + else if (std::string("\" \t*+").find(c_right) != std::string::npos) + str.erase(pos, 1); + else + str[pos] = '*'; + } + + std::vector group_start; + for (size_t pos = 0; pos < str.size(); pos++) { + if (str[pos] == '(') + group_start.push_back(pos); + if (str[pos] == ')' && group_start.size() > 0) { + if (pos+1 < str.size() && str[pos+1] == '\'') { + std::string group = str.substr(group_start.back(), pos-group_start.back()+1); + str[group_start.back()] = '~'; + str.replace(group_start.back()+1, group.size(), group); + pos++; + } + group_start.pop_back(); + } + if (str[pos] == '\'' && pos > 0) { + size_t start = str.find_last_of("()'*+^&| ", pos-1)+1; + std::string group = str.substr(start, pos-start); + str[start] = '~'; + str.replace(start+1, group.size(), group); + } + if (str[pos] == '*') + str[pos] = '&'; + if (str[pos] == '+') + str[pos] = '|'; + } + + return str; +} + +void event2vl(LibertyAst *ast, std::string &edge, std::string &expr) +{ + edge.clear(); + expr.clear(); + + if (ast != NULL) { + expr = func2vl(ast->value); + if (expr.size() > 0 && expr[0] == '~') + edge = "negedge " + expr.substr(1); + else + edge = "posedge " + expr; + } +} + +void clear_preset_var(std::string var, std::string type) +{ + if (type.find('L') != std::string::npos) { + printf(" %s <= 0;\n", var.c_str()); + return; + } + if (type.find('H') != std::string::npos) { + printf(" %s <= 1;\n", var.c_str()); + return; + } + if (type.find('T') != std::string::npos) { + printf(" %s <= ~%s;\n", var.c_str(), var.c_str()); + return; + } + if (type.find('X') != std::string::npos) { + printf(" %s <= 'bx;\n", var.c_str()); + return; + } +} + +void gen_verilogsim_cell(LibertyAst *ast) +{ + if (ast->find("statetable") != NULL) + return; + + CHECK_NV(ast->args.size(), == 1); + printf("module %s (", ast->args[0].c_str()); + bool first = true; + for (auto child : ast->children) { + if (child->id != "pin") + continue; + CHECK_NV(child->args.size(), == 1); + printf("%s%s", first ? "" : ", ", child->args[0].c_str()); + first = false; + } + printf(");\n"); + + for (auto child : ast->children) { + if (child->id != "ff" && child->id != "latch") + continue; + printf(" reg "); + first = true; + for (auto arg : child->args) { + printf("%s%s", first ? "" : ", ", arg.c_str()); + first = false; + } + printf(";\n"); + } + + for (auto child : ast->children) { + if (child->id != "pin") + continue; + CHECK_NV(child->args.size(), == 1); + LibertyAst *dir = CHECK(child->find("direction"), != NULL); + LibertyAst *func = child->find("function"); + printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str()); + if (func != NULL) + printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str()); + } + + for (auto child : ast->children) + { + if (child->id != "ff" || child->args.size() != 2) + continue; + + std::string iq_var = child->args[0]; + std::string iqn_var = child->args[1]; + + std::string clock_edge, clock_expr; + event2vl(child->find("clocked_on"), clock_edge, clock_expr); + + std::string clear_edge, clear_expr; + event2vl(child->find("clear"), clear_edge, clear_expr); + + std::string preset_edge, preset_expr; + event2vl(child->find("preset"), preset_edge, preset_expr); + + std::string edge = ""; + if (!clock_edge.empty()) + edge += (edge.empty() ? "" : ", ") + clock_edge; + if (!clear_edge.empty()) + edge += (edge.empty() ? "" : ", ") + clear_edge; + if (!preset_edge.empty()) + edge += (edge.empty() ? "" : ", ") + preset_edge; + + if (edge.empty()) + continue; + + printf(" always @(%s) begin\n", edge.c_str()); + + const char *else_prefix = ""; + if (!clear_expr.empty() && !preset_expr.empty()) { + printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str()); + clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value); + clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value); + printf(" end\n"); + else_prefix = "else "; + } + if (!clear_expr.empty()) { + printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str()); + printf(" %s <= 0;\n", iq_var.c_str()); + printf(" %s <= 1;\n", iqn_var.c_str()); + printf(" end\n"); + else_prefix = "else "; + } + if (!preset_expr.empty()) { + printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str()); + printf(" %s <= 1;\n", iq_var.c_str()); + printf(" %s <= 0;\n", iqn_var.c_str()); + printf(" end\n"); + else_prefix = "else "; + } + if (*else_prefix) + printf(" %sbegin\n", else_prefix); + std::string expr = CHECK(child->find("next_state"), != NULL)->value; + printf(" // %s\n", expr.c_str()); + printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str()); + printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str()); + if (*else_prefix) + printf(" end\n"); + + printf(" end\n"); + } + + for (auto child : ast->children) + { + if (child->id != "latch" || child->args.size() != 2) + continue; + + std::string iq_var = child->args[0]; + std::string iqn_var = child->args[1]; + + std::string enable_edge, enable_expr; + event2vl(child->find("enable"), enable_edge, enable_expr); + + std::string clear_edge, clear_expr; + event2vl(child->find("clear"), clear_edge, clear_expr); + + std::string preset_edge, preset_expr; + event2vl(child->find("preset"), preset_edge, preset_expr); + + printf(" always @* begin\n"); + + const char *else_prefix = ""; + if (!clear_expr.empty() && !preset_expr.empty()) { + printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str()); + clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value); + clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value); + printf(" end\n"); + else_prefix = "else "; + } + if (!clear_expr.empty()) { + printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str()); + printf(" %s <= 0;\n", iq_var.c_str()); + printf(" %s <= 1;\n", iqn_var.c_str()); + printf(" end\n"); + else_prefix = "else "; + } + if (!preset_expr.empty()) { + printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str()); + printf(" %s <= 1;\n", iq_var.c_str()); + printf(" %s <= 0;\n", iqn_var.c_str()); + printf(" end\n"); + else_prefix = "else "; + } + if (!enable_expr.empty()) { + printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str()); + std::string expr = CHECK(child->find("data_in"), != NULL)->value; + printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str()); + printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str()); + printf(" end\n"); + else_prefix = "else "; + } + + printf(" end\n"); + } + + printf("endmodule\n"); +} + +void gen_verilogsim(LibertyAst *ast) +{ + CHECK_COND(ast->id == "library"); + + for (auto child : ast->children) + if (child->id == "cell" && !child->find("dont_use")) + gen_verilogsim_cell(child); +} + +void usage() +{ + fprintf(stderr, "Usage: filterlib [rules-file [liberty-file]]\n"); + fprintf(stderr, " or: filterlib -verilogsim [liberty-file]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + bool flag_verilogsim = false; + + if (argc > 3) + usage(); + + if (argc > 1) + { + if (!strcmp(argv[1], "-verilogsim")) + flag_verilogsim = true; + if (!strcmp(argv[1], "-") || !strcmp(argv[1], "-verilogsim")) + { + LibertyAst::whitelist.insert("/library"); + LibertyAst::whitelist.insert("/library/cell"); + LibertyAst::whitelist.insert("/library/cell/area"); + LibertyAst::whitelist.insert("/library/cell/cell_footprint"); + LibertyAst::whitelist.insert("/library/cell/dont_touch"); + LibertyAst::whitelist.insert("/library/cell/dont_use"); + LibertyAst::whitelist.insert("/library/cell/ff"); + LibertyAst::whitelist.insert("/library/cell/ff/*"); + LibertyAst::whitelist.insert("/library/cell/latch"); + LibertyAst::whitelist.insert("/library/cell/latch/*"); + LibertyAst::whitelist.insert("/library/cell/pin"); + LibertyAst::whitelist.insert("/library/cell/pin/clock"); + LibertyAst::whitelist.insert("/library/cell/pin/direction"); + LibertyAst::whitelist.insert("/library/cell/pin/driver_type"); + LibertyAst::whitelist.insert("/library/cell/pin/function"); + LibertyAst::whitelist.insert("/library/cell/pin_opposite"); + LibertyAst::whitelist.insert("/library/cell/pin/state_function"); + LibertyAst::whitelist.insert("/library/cell/pin/three_state"); + LibertyAst::whitelist.insert("/library/cell/statetable"); + LibertyAst::whitelist.insert("/library/cell/statetable/*"); + } + else + { + FILE *f = fopen(argv[1], "r"); + if (f == NULL) { + fprintf(stderr, "Can't open rules file `%s'.\n", argv[1]); + usage(); + } + + char buffer[1024]; + while (fgets(buffer, 1024, f) != NULL) + { + char mode = 0; + std::string id; + for (char *p = buffer; *p; p++) + { + if (*p == '-' || *p == '+') { + if (mode != 0) + goto syntax_error; + mode = *p; + continue; + } + if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == '#') { + if (!id.empty()) { + if (mode == '-') + LibertyAst::blacklist.insert(id); + else + if (mode == '+') + LibertyAst::whitelist.insert(id); + else + goto syntax_error; + } + id.clear(); + if (*p == '#') + break; + continue; + } + id += *p; + continue; + + syntax_error: + fprintf(stderr, "Syntax error in rules file:\n%s", buffer); + exit(1); + } + } + } + } + + FILE *f = stdin; + if (argc == 3) { + f = fopen(argv[2], "r"); + if (f == NULL) { + fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]); + usage(); + } + } + + LibertyParer parser(f); + if (parser.ast) { + if (flag_verilogsim) + gen_verilogsim(parser.ast); + else + parser.ast->dump(stdout); + } + + if (argc == 3) + fclose(f); + + return 0; +} + +#endif + diff --git a/passes/dfflibmap/libparse.h b/passes/dfflibmap/libparse.h new file mode 100644 index 000000000..8c4a2f5ca --- /dev/null +++ b/passes/dfflibmap/libparse.h @@ -0,0 +1,56 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef LIBPARSE_H +#define LIBPARSE_H + +#include +#include +#include +#include + +namespace PASS_DFFLIBMAP +{ + struct LibertyAst + { + std::string id, value; + std::vector args; + std::vector children; + ~LibertyAst(); + LibertyAst *find(std::string name); + void dump(FILE *f, std::string indent = "", std::string path = "", bool path_ok = false); + static std::set blacklist; + static std::set whitelist; + }; + + struct LibertyParer + { + FILE *f; + int line; + LibertyAst *ast; + LibertyParer(FILE *f) : f(f), line(1), ast(parse()) {} + ~LibertyParer() { if (ast) delete ast; } + int lexer(std::string &str); + LibertyAst *parse(); + void error(); + }; +} + +#endif + diff --git a/passes/fsm/Makefile.inc b/passes/fsm/Makefile.inc new file mode 100644 index 000000000..38623e49e --- /dev/null +++ b/passes/fsm/Makefile.inc @@ -0,0 +1,11 @@ + +OBJS += passes/fsm/fsm.o +OBJS += passes/fsm/fsm_detect.o +OBJS += passes/fsm/fsm_extract.o +OBJS += passes/fsm/fsm_opt.o +OBJS += passes/fsm/fsm_expand.o +OBJS += passes/fsm/fsm_recode.o +OBJS += passes/fsm/fsm_info.o +OBJS += passes/fsm/fsm_export.o +OBJS += passes/fsm/fsm_map.o + diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc new file mode 100644 index 000000000..61322fbd3 --- /dev/null +++ b/passes/fsm/fsm.cc @@ -0,0 +1,91 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +struct FsmPass : public Pass { + FsmPass() : Pass("fsm") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + bool flag_nomap = false; + bool flag_norecode = false; + bool flag_expand = false; + bool flag_export = false; + std::string fm_set_fsm_file_opt; + + log_header("Executing FSM pass (extract and optimize FSM).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file_opt.empty()) { + fm_set_fsm_file_opt = " -fm_set_fsm_file " + args[++argidx]; + continue; + } + if (arg == "-norecode") { + flag_norecode = true; + continue; + } + if (arg == "-nomap") { + flag_nomap = true; + continue; + } + if (arg == "-expand") { + flag_expand = true; + continue; + } + if (arg == "-export") { + flag_export = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + Pass::call(design, "fsm_detect"); + Pass::call(design, "fsm_extract"); + + Pass::call(design, "fsm_opt"); + Pass::call(design, "opt_rmunused"); + Pass::call(design, "fsm_opt"); + + if (flag_expand) { + Pass::call(design, "fsm_expand"); + Pass::call(design, "opt_rmunused"); + Pass::call(design, "fsm_opt"); + } + + if (!flag_norecode) + Pass::call(design, "fsm_recode" + fm_set_fsm_file_opt); + Pass::call(design, "fsm_info"); + + if (!flag_nomap) + Pass::call(design, "fsm_map"); + + if (flag_export) + Pass::call(design, "fsm_export"); + + log_pop(); + } +} FsmPass; + diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc new file mode 100644 index 000000000..067ed171f --- /dev/null +++ b/passes/fsm/fsm_detect.cc @@ -0,0 +1,160 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" + +static RTLIL::Module *module; +static SigMap assign_map; +typedef std::pair sig2driver_entry_t; +static SigSet sig2driver, sig2user; +static std::set muxtree_cells; +static SigPool sig_at_port; + +static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig) +{ + if (sig_at_port.check_any(assign_map(sig))) + return false; + + if (sig.is_fully_const() || old_sig == sig) + return true; + + std::set cellport_list; + sig2driver.find(sig, cellport_list); + for (auto &cellport : cellport_list) { + if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y") + return false; + RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]); + RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]); + if (!check_state_mux_tree(old_sig, sig_a)) + return false; + for (int i = 0; i < sig_b.width; i += sig_a.width) + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.width))) + return false; + muxtree_cells.insert(cellport.first); + } + + return true; +} + +static bool check_state_users(RTLIL::SigSpec sig) +{ + if (sig_at_port.check_any(assign_map(sig))) + return false; + + std::set cellport_list; + sig2user.find(sig, cellport_list); + for (auto &cellport : cellport_list) { + RTLIL::Cell *cell = cellport.first; + if (muxtree_cells.count(cell) > 0) + continue; + if (cellport.second != "\\A" && cellport.second != "\\B") + return false; + if (cell->connections.count("\\A") == 0 || cell->connections.count("\\B") == 0 || cell->connections.count("\\Y") == 0) + return false; + for (auto &port_it : cell->connections) + if (port_it.first != "\\A" && port_it.first != "\\B" && port_it.first != "\\Y") + return false; + if (assign_map(cell->connections["\\A"]) == sig && cell->connections["\\B"].is_fully_const()) + continue; + if (assign_map(cell->connections["\\B"]) == sig && cell->connections["\\A"].is_fully_const()) + continue; + return false; + } + + return true; +} + +static void detect_fsm(RTLIL::Wire *wire) +{ + if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1) + return; + if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire)))) + return; + + std::set cellport_list; + sig2driver.find(RTLIL::SigSpec(wire), cellport_list); + for (auto &cellport : cellport_list) { + if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q") + continue; + muxtree_cells.clear(); + RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]); + RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]); + if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d) && check_state_users(sig_q)) { + log("Found FSM state register %s in module %s.\n", wire->name.c_str(), module->name.c_str()); + wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto"); + return; + } + } +} + +struct FsmDetectPass : public Pass { + FsmDetectPass() : Pass("fsm_detect") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing FSM_DETECT pass (finding FSMs in design).\n"); + extra_args(args, 1, design); + + CellTypes ct; + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + for (auto &mod_it : design->modules) + { + module = mod_it.second; + assign_map.set(module); + + sig2driver.clear(); + sig2user.clear(); + sig_at_port.clear(); + for (auto &cell_it : module->cells) + for (auto &conn_it : cell_it.second->connections) { + if (ct.cell_output(cell_it.second->type, conn_it.first)) { + RTLIL::SigSpec sig = conn_it.second; + assign_map.apply(sig); + sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first)); + } + if (!ct.cell_known(cell_it.second->type) || ct.cell_input(cell_it.second->type, conn_it.first)) { + RTLIL::SigSpec sig = conn_it.second; + assign_map.apply(sig); + sig2user.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first)); + } + } + + for (auto &wire_it : module->wires) + if (wire_it.second->port_id != 0) + sig_at_port.add(assign_map(RTLIL::SigSpec(wire_it.second))); + + for (auto &wire_it : module->wires) + detect_fsm(wire_it.second); + } + + assign_map.clear(); + sig2driver.clear(); + sig2user.clear(); + muxtree_cells.clear(); + } +} FsmDetectPass; + diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc new file mode 100644 index 000000000..0d5bc7695 --- /dev/null +++ b/passes/fsm/fsm_expand.cc @@ -0,0 +1,255 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" +#include + +struct FsmExpand +{ + RTLIL::Module *module; + RTLIL::Cell *fsm_cell; + SigMap assign_map; + SigSet sig2driver, sig2user; + CellTypes ct; + + std::set merged_set; + std::set current_set; + std::set no_candidate_set; + + bool already_optimized; + int limit_transitions; + + bool is_cell_merge_candidate(RTLIL::Cell *cell) + { + RTLIL::SigSpec new_signals; + if (cell->connections.count("\\A") > 0) + new_signals.append(assign_map(cell->connections["\\A"])); + if (cell->connections.count("\\B") > 0) + new_signals.append(assign_map(cell->connections["\\B"])); + if (cell->connections.count("\\S") > 0) + new_signals.append(assign_map(cell->connections["\\S"])); + + new_signals.sort_and_unify(); + new_signals.remove_const(); + + if (new_signals.width > 4) + return false; + + new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"])); + new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"])); + + if (cell->connections.count("\\Y") > 0) { + new_signals.append(assign_map(cell->connections["\\Y"])); + new_signals.sort_and_unify(); + new_signals.remove_const(); + new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_IN"])); + new_signals.remove(assign_map(fsm_cell->connections["\\CTRL_OUT"])); + } + + if (new_signals.width > 2) + return false; + + return true; + } + + void create_current_set() + { + std::vector cell_list; + + for (auto c : sig2driver.find(assign_map(fsm_cell->connections["\\CTRL_IN"]))) + cell_list.push_back(c); + + for (auto c : sig2user.find(assign_map(fsm_cell->connections["\\CTRL_OUT"]))) + cell_list.push_back(c); + + current_set.clear(); + for (auto c : cell_list) + { + if (merged_set.count(c) > 0 || current_set.count(c) > 0 || no_candidate_set.count(c) > 0) + continue; + for (auto &p : c->connections) { + if (p.first != "\\A" && p.first != "\\B" && p.first != "\\S" && p.first != "\\Y") + goto next_cell; + } + if (!is_cell_merge_candidate(c)) { + no_candidate_set.insert(c); + continue; + } + current_set.insert(c); + next_cell:; + } + } + + void optimze_as_needed() + { + if (already_optimized) + return; + + int trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int(); + if (trans_num > limit_transitions) + { + log(" grown transition table to %d entries -> optimize.\n", trans_num); + FsmData::optimize_fsm(fsm_cell, module); + already_optimized = true; + + trans_num = fsm_cell->parameters["\\TRANS_NUM"].as_int(); + log(" transition table size after optimizaton: %d\n", trans_num); + limit_transitions = 16 * trans_num; + } + } + + void merge_cell_into_fsm(RTLIL::Cell *cell) + { + optimze_as_needed(); + + log(" merging %s cell %s.\n", cell->type.c_str(), cell->name.c_str()); + merged_set.insert(cell); + already_optimized = false; + + RTLIL::SigSpec input_sig, output_sig; + + for (auto &p : cell->connections) + if (ct.cell_output(cell->type, p.first)) + output_sig.append(assign_map(p.second)); + else + input_sig.append(assign_map(p.second)); + input_sig.sort_and_unify(); + input_sig.remove_const(); + + assert(input_sig.width <= 4); + std::vector truth_tab; + + for (int i = 0; i < (1 << input_sig.width); i++) { + RTLIL::Const in_val(i, input_sig.width); + RTLIL::SigSpec A, B, S; + if (cell->connections.count("\\A") > 0) + A = assign_map(cell->connections["\\A"]); + if (cell->connections.count("\\B") > 0) + B = assign_map(cell->connections["\\B"]); + if (cell->connections.count("\\S") > 0) + S = assign_map(cell->connections["\\S"]); + A.replace(input_sig, RTLIL::SigSpec(in_val)); + B.replace(input_sig, RTLIL::SigSpec(in_val)); + S.replace(input_sig, RTLIL::SigSpec(in_val)); + assert(A.is_fully_const()); + assert(B.is_fully_const()); + assert(S.is_fully_const()); + truth_tab.push_back(ct.eval(cell, A.as_const(), B.as_const(), S.as_const())); + } + + FsmData fsm_data; + fsm_data.copy_from_cell(fsm_cell); + + fsm_data.num_inputs += input_sig.width; + fsm_cell->connections["\\CTRL_IN"].append(input_sig); + + fsm_data.num_outputs += output_sig.width; + fsm_cell->connections["\\CTRL_OUT"].append(output_sig); + + std::vector new_transition_table; + for (auto &tr : fsm_data.transition_table) { + for (int i = 0; i < (1 << input_sig.width); i++) { + FsmData::transition_t new_tr = tr; + RTLIL::Const in_val(i, input_sig.width); + RTLIL::Const out_val = truth_tab[i]; + RTLIL::SigSpec ctrl_in = new_tr.ctrl_in; + RTLIL::SigSpec ctrl_out = new_tr.ctrl_out; + ctrl_in.append(in_val); + ctrl_out.append(out_val); + new_tr.ctrl_in = ctrl_in.as_const(); + new_tr.ctrl_out = ctrl_out.as_const(); + new_transition_table.push_back(new_tr); + } + } + fsm_data.transition_table.swap(new_transition_table); + new_transition_table.clear(); + + fsm_data.copy_to_cell(fsm_cell); + } + + FsmExpand(RTLIL::Cell *cell, RTLIL::Module *mod) + { + module = mod; + fsm_cell = cell; + + assign_map.set(module); + ct.setup_internals(); + + for (auto &cell_it : module->cells) { + RTLIL::Cell *c = cell_it.second; + if (ct.cell_known(c->type)) + for (auto &p : c->connections) { + if (ct.cell_output(c->type, p.first)) + sig2driver.insert(assign_map(p.second), c); + else + sig2user.insert(assign_map(p.second), c); + } + } + } + + void execute() + { + log("\n"); + log("Expanding FSM `%s' from module `%s':\n", fsm_cell->name.c_str(), module->name.c_str()); + + already_optimized = false; + limit_transitions = 16 * fsm_cell->parameters["\\TRANS_NUM"].as_int(); + + for (create_current_set(); current_set.size() > 0; create_current_set()) { + for (auto c : current_set) + merge_cell_into_fsm(c); + } + + for (auto c : merged_set) { + module->cells.erase(c->name); + delete c; + } + + if (merged_set.size() > 0 && !already_optimized) + FsmData::optimize_fsm(fsm_cell, module); + + log(" merged %zd cells into FSM.\n", merged_set.size()); + } +}; + +struct FsmExpandPass : public Pass { + FsmExpandPass() : Pass("fsm_expand") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing FSM_EXPAND pass (re-assigning FSM state encoding).\n"); + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) { + std::vector fsm_cells; + for (auto &cell_it : mod_it.second->cells) + if (cell_it.second->type == "$fsm") + fsm_cells.push_back(cell_it.second); + for (auto c : fsm_cells) { + FsmExpand fsm_expand(c, mod_it.second); + fsm_expand.execute(); + } + } + } +} FsmExpandPass; + diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc new file mode 100644 index 000000000..0219f4ebd --- /dev/null +++ b/passes/fsm/fsm_export.cc @@ -0,0 +1,103 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Martin Schmölzer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" +#include +#include +#include + +/** + * Convert signal into a KISS-compatible textual representation. + */ +std::string kiss_convert_signal(const RTLIL::SigSpec &sig) { + if (!sig.is_fully_const()) { + throw 0; + } + + return sig.as_const().as_string(); +} + +/** + * Exports each Finite State Machine (FSM) in the design to a file in KISS2 format. + */ +struct FsmExportPass : public Pass { + FsmExportPass() : Pass("fsm_export") { + } + + virtual void execute(std::vector args, RTLIL::Design *design) + { + FsmData fsm_data; + std::string kiss_name; + std::ofstream kiss_file; + size_t i; + FsmData::transition_t tr; + + log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + for (auto &cell_it : mod_it.second->cells) + if (cell_it.second->type == "$fsm") { + kiss_name.assign(mod_it.first.c_str()); + kiss_name.append("-" + cell_it.second->name + ".kiss2"); + fsm_data.copy_from_cell(cell_it.second); + + log("\n"); + log("Exporting FSM `%s' from module `%s' to file `%s'.\n", + cell_it.second->name.c_str(), + mod_it.first.c_str(), + kiss_name.c_str()); + + kiss_file.open(kiss_name, std::ios::out | std::ios::trunc); + + if (!kiss_file.is_open()) { + log_error("Could not open file \"%s\" with write access.\n", kiss_name.c_str()); + return; + } + + kiss_file << ".start_kiss" << std::endl; + kiss_file << ".i " << std::dec << fsm_data.num_inputs << std::endl; + kiss_file << ".o " << std::dec << fsm_data.num_outputs << std::endl; + kiss_file << ".r s" << std::dec << fsm_data.reset_state << std::endl; + + for (i = 0; i < fsm_data.transition_table.size(); i++) { + tr = fsm_data.transition_table[i]; + + try { + kiss_file << kiss_convert_signal(tr.ctrl_in) << ' '; + kiss_file << 's' << tr.state_in << ' '; + kiss_file << 's' << tr.state_out << ' '; + kiss_file << kiss_convert_signal(tr.ctrl_out) << std::endl; + } + catch (int) { + log_error("exporting an FSM input or output signal failed.\n"); + } + } + + kiss_file << ".end_kiss" << std::endl << ".end" << std::endl; + kiss_file.close(); + } + } +} FsmExportPass; diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc new file mode 100644 index 000000000..bdcb1d45a --- /dev/null +++ b/passes/fsm/fsm_extract.cc @@ -0,0 +1,359 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" + +static RTLIL::Module *module; +static SigMap assign_map; +typedef std::pair sig2driver_entry_t; +static SigSet sig2driver, sig2trigger; + +static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL::SigSpec &ctrl, std::map &states, RTLIL::Const *reset_state = NULL) +{ + sig.extend(dff_out.width, false); + + if (sig == dff_out) + return true; + + assign_map.apply(sig); + if (sig.is_fully_const()) { + sig.optimize(); + assert(sig.chunks.size() == 1); + if (states.count(sig.chunks[0].data) == 0) { + log(" found state code: %s\n", log_signal(sig)); + states[sig.chunks[0].data] = -1; + } + return true; + } + + std::set cellport_list; + sig2driver.find(sig, cellport_list); + for (auto &cellport : cellport_list) { + if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux" && cellport.first->type != "$safe_pmux") || cellport.second != "\\Y") { + log(" unexpected cell type %s (%s) found in state selection tree.\n", + cellport.first->type.c_str(), cellport.first->name.c_str()); + return false; + } + RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]); + RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]); + RTLIL::SigSpec sig_s = assign_map(cellport.first->connections["\\S"]); + if (reset_state && RTLIL::SigSpec(*reset_state).is_fully_undef()) + do { + if (sig_a.is_fully_def()) + *reset_state = sig_a.as_const(); + else if (sig_b.is_fully_def()) + *reset_state = sig_b.as_const(); + else + break; + log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state)); + } while (0); + if (ctrl.extract(sig_s).width == 0) { + log(" found ctrl input: %s\n", log_signal(sig_s)); + ctrl.append(sig_s); + } + if (!find_states(sig_a, dff_out, ctrl, states)) + return false; + for (int i = 0; i < sig_b.width/sig_a.width; i++) { + if (!find_states(sig_b.extract(i*sig_a.width, sig_a.width), dff_out, ctrl, states)) + return false; + } + } + + return true; +} + +static RTLIL::Const sig2const(ConstEval &ce, RTLIL::SigSpec sig, RTLIL::State noconst_state, RTLIL::SigSpec dont_care = RTLIL::SigSpec()) +{ + if (dont_care.width > 0) { + sig.expand(); + for (auto &chunk : sig.chunks) { + assert(chunk.width == 1); + if (dont_care.extract(chunk).width > 0) + chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state); + } + sig.optimize(); + } + + ce.assign_map.apply(sig); + ce.values_map.apply(sig); + + sig.expand(); + for (auto &chunk : sig.chunks) { + assert(chunk.width == 1); + if (chunk.wire != NULL) + chunk.wire = NULL, chunk.data = RTLIL::Const(noconst_state); + } + sig.optimize(); + + if (sig.width == 0) + return RTLIL::Const(); + assert(sig.chunks.size() == 1 && sig.chunks[0].wire == NULL); + return sig.chunks[0].data; +} + +static void find_transitions(ConstEval &ce, ConstEval &ce_nostop, FsmData &fsm_data, std::map &states, int state_in, RTLIL::SigSpec ctrl_in, RTLIL::SigSpec ctrl_out, RTLIL::SigSpec dff_in, RTLIL::SigSpec dont_care) +{ + RTLIL::SigSpec undef, constval; + + if (ce.eval(ctrl_out, undef) && ce.eval(dff_in, undef)) { + assert(ctrl_out.is_fully_const() && dff_in.is_fully_const()); + FsmData::transition_t tr; + tr.state_in = state_in; + tr.state_out = states[ce.values_map(ce.assign_map(dff_in)).as_const()]; + tr.ctrl_in = sig2const(ce, ctrl_in, RTLIL::State::Sa, dont_care); + tr.ctrl_out = sig2const(ce, ctrl_out, RTLIL::State::Sx); + RTLIL::Const log_state_in = RTLIL::Const(RTLIL::State::Sx, fsm_data.state_bits); + if (state_in >= 0) + log_state_in = fsm_data.state_table[tr.state_in]; + if (dff_in.is_fully_def()) { + fsm_data.transition_table.push_back(tr); + log(" transition: %10s %s -> %10s %s\n", + log_signal(log_state_in), log_signal(tr.ctrl_in), + log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out)); + } else { + log(" transition: %10s %s -> %10s %s \n", + log_signal(log_state_in), log_signal(tr.ctrl_in), + log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out)); + } + return; + } + + assert(undef.width > 0); + assert(ce.stop_signals.check_all(undef)); + + undef = undef.extract(0, 1); + constval = undef; + + if (ce_nostop.eval(constval)) + { + ce.push(); + dont_care.append(undef); + ce.set(undef, constval.as_const()); + find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care); + ce.pop(); + } + else + { + ce.push(), ce_nostop.push(); + ce.set(undef, RTLIL::Const(0, 1)); + ce_nostop.set(undef, RTLIL::Const(0, 1)); + find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care); + ce.pop(), ce_nostop.pop(); + + ce.push(), ce_nostop.push(); + ce.set(undef, RTLIL::Const(1, 1)); + ce_nostop.set(undef, RTLIL::Const(1, 1)); + find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care); + ce.pop(), ce_nostop.pop(); + } +} + +static void extract_fsm(RTLIL::Wire *wire) +{ + log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str()); + + // get input and output signals for state ff + + RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire)); + RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width); + RTLIL::Const reset_state(RTLIL::State::Sx, wire->width); + + RTLIL::SigSpec clk = RTLIL::SigSpec(0, 1); + RTLIL::SigSpec arst = RTLIL::SigSpec(0, 1); + bool clk_polarity = true; + bool arst_polarity = true; + + std::set cellport_list; + sig2driver.find(dff_out, cellport_list); + for (auto &cellport : cellport_list) { + if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q") + continue; + log(" found %s cell for state register: %s\n", cellport.first->type.c_str(), cellport.first->name.c_str()); + RTLIL::SigSpec sig_q = assign_map(cellport.first->connections["\\Q"]); + RTLIL::SigSpec sig_d = assign_map(cellport.first->connections["\\D"]); + clk = cellport.first->connections["\\CLK"]; + clk_polarity = cellport.first->parameters["\\CLK_POLARITY"].as_bool(); + if (cellport.first->type == "$adff") { + arst = cellport.first->connections["\\ARST"]; + arst_polarity = cellport.first->parameters["\\ARST_POLARITY"].as_bool(); + reset_state = cellport.first->parameters["\\ARST_VALUE"]; + } + sig_q.replace(dff_out, sig_d, &dff_in); + break; + } + + log(" root of input selection tree: %s\n", log_signal(dff_in)); + if (dff_in.has_marked_bits()) { + log(" fsm extraction failed: incomplete input selection tree root.\n"); + return; + } + + // find states and control inputs + + RTLIL::SigSpec ctrl_in; + std::map states; + if (!arst.is_fully_const()) { + log(" found reset state: %s (from async reset)\n", log_signal(reset_state)); + states[reset_state] = -1; + } + if (!find_states(dff_in, dff_out, ctrl_in, states, &reset_state)) { + log(" fsm extraction failed: state selection tree is not closed.\n"); + return; + } + + // find control outputs + // (add the state signals to the list of control outputs. if everything goes right, this signals + // become unused and can then be removed from the fsm control output) + + RTLIL::SigSpec ctrl_out = dff_in; + cellport_list.clear(); + sig2trigger.find(dff_out, cellport_list); + for (auto &cellport : cellport_list) { + RTLIL::SigSpec sig_a = assign_map(cellport.first->connections["\\A"]); + RTLIL::SigSpec sig_b = assign_map(cellport.first->connections["\\B"]); + RTLIL::SigSpec sig_y = assign_map(cellport.first->connections["\\Y"]); + if (cellport.second == "\\A" && !sig_b.is_fully_const()) + continue; + if (cellport.second == "\\B" && !sig_a.is_fully_const()) + continue; + log(" found ctrl output: %s\n", log_signal(sig_y)); + ctrl_out.append(sig_y); + } + ctrl_in.remove(ctrl_out); + + log(" ctrl inputs: %s\n", log_signal(ctrl_in)); + log(" ctrl outputs: %s\n", log_signal(ctrl_out)); + + // Initialize fsm data struct + + FsmData fsm_data; + fsm_data.num_inputs = ctrl_in.width; + fsm_data.num_outputs = ctrl_out.width; + fsm_data.state_bits = wire->width; + fsm_data.reset_state = -1; + for (auto &it : states) { + it.second = fsm_data.state_table.size(); + fsm_data.state_table.push_back(it.first); + } + if (!arst.is_fully_const() || RTLIL::SigSpec(reset_state).is_fully_def()) + fsm_data.reset_state = states[reset_state]; + + // Create transition table + + ConstEval ce(module), ce_nostop(module); + ce.stop(ctrl_in); + for (int state_idx = 0; state_idx < int(fsm_data.state_table.size()); state_idx++) { + ce.push(), ce_nostop.push(); + ce.set(dff_out, fsm_data.state_table[state_idx]); + ce_nostop.set(dff_out, fsm_data.state_table[state_idx]); + find_transitions(ce, ce_nostop, fsm_data, states, state_idx, ctrl_in, ctrl_out, dff_in, RTLIL::SigSpec()); + ce.pop(), ce_nostop.pop(); + } + + // create fsm cell + + RTLIL::Cell *fsm_cell = new RTLIL::Cell; + fsm_cell->name = stringf("$fsm$%s$%d", wire->name.c_str(), RTLIL::autoidx++); + fsm_cell->type = "$fsm"; + fsm_cell->connections["\\CLK"] = clk; + fsm_cell->connections["\\ARST"] = arst; + fsm_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity ? 1 : 0, 1); + fsm_cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity ? 1 : 0, 1); + fsm_cell->connections["\\CTRL_IN"] = ctrl_in; + fsm_cell->connections["\\CTRL_OUT"] = ctrl_out; + fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name); + fsm_data.copy_to_cell(fsm_cell); + module->cells[fsm_cell->name] = fsm_cell; + + // rename original state wire + + module->wires.erase(wire->name); + wire->attributes.erase("\\fsm_encoding"); + wire->name = stringf("$fsm$oldstate%s", wire->name.c_str()); + module->wires[wire->name] = wire; + + // unconnect control outputs from old drivers + + cellport_list.clear(); + sig2driver.find(ctrl_out, cellport_list); + for (auto &cellport : cellport_list) { + RTLIL::SigSpec port_sig = assign_map(cellport.first->connections[cellport.second]); + RTLIL::SigSpec unconn_sig = port_sig.extract(ctrl_out); + RTLIL::Wire *unconn_wire = new RTLIL::Wire; + unconn_wire->name = stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), RTLIL::autoidx++); + unconn_wire->width = unconn_sig.width; + module->wires[unconn_wire->name] = unconn_wire; + port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cellport.first->connections[cellport.second]); + } +} + +struct FsmExtractPass : public Pass { + FsmExtractPass() : Pass("fsm_extract") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n"); + extra_args(args, 1, design); + + CellTypes ct; + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + for (auto &mod_it : design->modules) + { + module = mod_it.second; + assign_map.set(module); + + sig2driver.clear(); + sig2trigger.clear(); + for (auto &cell_it : module->cells) + for (auto &conn_it : cell_it.second->connections) { + if (ct.cell_output(cell_it.second->type, conn_it.first)) { + RTLIL::SigSpec sig = conn_it.second; + assign_map.apply(sig); + sig2driver.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first)); + } + if (ct.cell_input(cell_it.second->type, conn_it.first) && cell_it.second->connections.count("\\Y") > 0 && + cell_it.second->connections["\\Y"].width == 1 && (conn_it.first == "\\A" || conn_it.first == "\\B")) { + RTLIL::SigSpec sig = conn_it.second; + assign_map.apply(sig); + sig2trigger.insert(sig, sig2driver_entry_t(cell_it.second, conn_it.first)); + } + } + + std::vector wire_list; + for (auto &wire_it : module->wires) + if (wire_it.second->attributes.count("\\fsm_encoding") > 0 && wire_it.second->attributes["\\fsm_encoding"].str != "none") + wire_list.push_back(wire_it.second); + for (auto wire : wire_list) + extract_fsm(wire); + } + + assign_map.clear(); + sig2driver.clear(); + sig2trigger.clear(); + } +} FsmExtractPass; + diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc new file mode 100644 index 000000000..83f065760 --- /dev/null +++ b/passes/fsm/fsm_info.cc @@ -0,0 +1,46 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" +#include + +struct FsmInfoPass : public Pass { + FsmInfoPass() : Pass("fsm_info") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + for (auto &cell_it : mod_it.second->cells) + if (cell_it.second->type == "$fsm") { + log("\n"); + log("FSM `%s' from module `%s':\n", cell_it.second->name.c_str(), mod_it.first.c_str()); + FsmData fsm_data; + fsm_data.copy_from_cell(cell_it.second); + fsm_data.log_info(cell_it.second); + } + } +} FsmInfoPass; + diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc new file mode 100644 index 000000000..4319dc044 --- /dev/null +++ b/passes/fsm/fsm_map.cc @@ -0,0 +1,356 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" +#include + +static void implement_pattern_cache(RTLIL::Module *module, std::map> &pattern_cache, std::set &fullstate_cache, int num_states, RTLIL::Wire *state_onehot, RTLIL::SigSpec &ctrl_in, RTLIL::SigSpec output) +{ + RTLIL::SigSpec cases_vector; + + for (int in_state : fullstate_cache) + cases_vector.append(RTLIL::SigSpec(state_onehot, 1, in_state)); + + for (auto &it : pattern_cache) + { + RTLIL::Const pattern = it.first; + RTLIL::SigSpec eq_sig_a, eq_sig_b, or_sig; + + for (size_t j = 0; j < pattern.bits.size(); j++) + if (pattern.bits[j] == RTLIL::State::S0 || pattern.bits[j] == RTLIL::State::S1) { + eq_sig_a.append(ctrl_in.extract(j, 1)); + eq_sig_b.append(RTLIL::SigSpec(pattern.bits[j])); + } + eq_sig_a.optimize(); + eq_sig_b.optimize(); + + for (int in_state : it.second) + if (fullstate_cache.count(in_state) == 0) + or_sig.append(RTLIL::SigSpec(state_onehot, 1, in_state)); + or_sig.optimize(); + + if (or_sig.width == 0) + continue; + + RTLIL::SigSpec and_sig; + + if (eq_sig_a.width > 0) + { + RTLIL::Wire *eq_wire = new RTLIL::Wire; + eq_wire->name = NEW_ID; + module->add(eq_wire); + + RTLIL::Cell *eq_cell = new RTLIL::Cell; + eq_cell->name = NEW_ID; + eq_cell->type = "$eq"; + eq_cell->connections["\\A"] = eq_sig_a; + eq_cell->connections["\\B"] = eq_sig_b; + eq_cell->connections["\\Y"] = RTLIL::SigSpec(eq_wire); + eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); + eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false); + eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(eq_sig_a.width); + eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(eq_sig_b.width); + eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + module->add(eq_cell); + + and_sig.append(RTLIL::SigSpec(eq_wire)); + } + + if (or_sig.width == 1) + { + and_sig.append(or_sig); + } + else if (or_sig.width < num_states && int(it.second.size()) < num_states) + { + RTLIL::Wire *or_wire = new RTLIL::Wire; + or_wire->name = NEW_ID; + module->add(or_wire); + + RTLIL::Cell *or_cell = new RTLIL::Cell; + or_cell->name = NEW_ID; + or_cell->type = "$reduce_or"; + or_cell->connections["\\A"] = or_sig; + or_cell->connections["\\Y"] = RTLIL::SigSpec(or_wire); + or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); + or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(or_sig.width); + or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + module->add(or_cell); + + and_sig.append(RTLIL::SigSpec(or_wire)); + } + + switch (and_sig.width) + { + case 2: + { + RTLIL::Wire *and_wire = new RTLIL::Wire; + and_wire->name = NEW_ID; + module->add(and_wire); + + RTLIL::Cell *and_cell = new RTLIL::Cell; + and_cell->name = NEW_ID; + and_cell->type = "$and"; + and_cell->connections["\\A"] = and_sig.extract(0, 1); + and_cell->connections["\\B"] = and_sig.extract(1, 1); + and_cell->connections["\\Y"] = RTLIL::SigSpec(and_wire); + and_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); + and_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false); + and_cell->parameters["\\A_WIDTH"] = RTLIL::Const(1); + and_cell->parameters["\\B_WIDTH"] = RTLIL::Const(1); + and_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + module->add(and_cell); + + cases_vector.append(RTLIL::SigSpec(and_wire)); + break; + } + case 1: + cases_vector.append(and_sig); + break; + case 0: + cases_vector.append(RTLIL::SigSpec(1, 1)); + break; + default: + assert(!"This should never happen!"); + } + } + + if (cases_vector.width > 1) { + RTLIL::Cell *or_cell = new RTLIL::Cell; + or_cell->name = NEW_ID; + or_cell->type = "$reduce_or"; + or_cell->connections["\\A"] = cases_vector; + or_cell->connections["\\Y"] = output; + or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); + or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cases_vector.width); + or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + module->add(or_cell); + } else if (cases_vector.width == 1) { + module->connections.push_back(RTLIL::SigSig(output, cases_vector)); + } else { + module->connections.push_back(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1))); + } +} + +static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) +{ + log("Mapping FSM `%s' from module `%s'.\n", fsm_cell->name.c_str(), module->name.c_str()); + + FsmData fsm_data; + fsm_data.copy_from_cell(fsm_cell); + + RTLIL::SigSpec ctrl_in = fsm_cell->connections["\\CTRL_IN"]; + RTLIL::SigSpec ctrl_out = fsm_cell->connections["\\CTRL_OUT"]; + + // create state register + + RTLIL::Wire *state_wire = new RTLIL::Wire; + state_wire->name = fsm_cell->parameters["\\NAME"].str; + while (module->count_id(state_wire->name) > 0) + state_wire->name += "_"; + state_wire->width = fsm_data.state_bits; + module->add(state_wire); + + RTLIL::Wire *next_state_wire = new RTLIL::Wire; + next_state_wire->name = NEW_ID; + next_state_wire->width = fsm_data.state_bits; + module->add(next_state_wire); + + RTLIL::Cell *state_dff = new RTLIL::Cell; + state_dff->name = NEW_ID; + if (fsm_cell->connections["\\ARST"].is_fully_const()) { + state_dff->type = "$dff"; + } else { + state_dff->type = "$adff"; + state_dff->parameters["\\ARST_POLARITY"] = fsm_cell->parameters["\\ARST_POLARITY"]; + state_dff->parameters["\\ARST_VALUE"] = fsm_data.state_table[fsm_data.reset_state]; + state_dff->connections["\\ARST"] = fsm_cell->connections["\\ARST"]; + } + state_dff->parameters["\\WIDTH"] = RTLIL::Const(fsm_data.state_bits); + state_dff->parameters["\\CLK_POLARITY"] = fsm_cell->parameters["\\CLK_POLARITY"]; + state_dff->connections["\\CLK"] = fsm_cell->connections["\\CLK"]; + state_dff->connections["\\D"] = RTLIL::SigSpec(next_state_wire); + state_dff->connections["\\Q"] = RTLIL::SigSpec(state_wire); + module->add(state_dff); + + // decode state register + + bool encoding_is_onehot = true; + + RTLIL::Wire *state_onehot = new RTLIL::Wire; + state_onehot->name = NEW_ID; + state_onehot->width = fsm_data.state_table.size(); + module->add(state_onehot); + + for (size_t i = 0; i < fsm_data.state_table.size(); i++) + { + RTLIL::Const state = fsm_data.state_table[i]; + RTLIL::SigSpec sig_a, sig_b; + + for (size_t j = 0; j < state.bits.size(); j++) + if (state.bits[j] == RTLIL::State::S0 || state.bits[j] == RTLIL::State::S1) { + sig_a.append(RTLIL::SigSpec(state_wire, 1, j)); + sig_b.append(RTLIL::SigSpec(state.bits[j])); + } + sig_a.optimize(); + sig_b.optimize(); + + if (sig_b == RTLIL::SigSpec(RTLIL::State::S1)) + { + module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(state_onehot, 1, i), sig_a)); + } + else + { + if (sig_b.as_bool() || sig_b.width != fsm_data.state_bits) + encoding_is_onehot = false; + + RTLIL::Cell *eq_cell = new RTLIL::Cell; + eq_cell->name = NEW_ID; + eq_cell->type = "$eq"; + eq_cell->connections["\\A"] = sig_a; + eq_cell->connections["\\B"] = sig_b; + eq_cell->connections["\\Y"] = RTLIL::SigSpec(state_onehot, 1, i); + eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(false); + eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(false); + eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig_a.width); + eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(sig_b.width); + eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + module->add(eq_cell); + } + } + + // generate next_state signal + + RTLIL::Wire *next_state_onehot = new RTLIL::Wire; + next_state_onehot->name = NEW_ID; + next_state_onehot->width = fsm_data.state_table.size(); + module->add(next_state_onehot); + + for (size_t i = 0; i < fsm_data.state_table.size(); i++) + { + std::map> pattern_cache; + std::set fullstate_cache; + + for (size_t j = 0; j < fsm_data.state_table.size(); j++) + fullstate_cache.insert(j); + + for (auto &tr : fsm_data.transition_table) { + if (tr.state_out == int(i)) + pattern_cache[tr.ctrl_in].insert(tr.state_in); + else + fullstate_cache.erase(tr.state_in); + } + + implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, RTLIL::SigSpec(next_state_onehot, 1, i)); + } + + if (encoding_is_onehot) + { + for (size_t i = 0; i < fsm_data.state_table.size(); i++) { + RTLIL::Const state = fsm_data.state_table[i]; + int bit_idx = -1; + for (size_t j = 0; j < state.bits.size(); j++) + if (state.bits[j] == RTLIL::State::S1) + bit_idx = j; + if (bit_idx >= 0) + module->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(next_state_wire, 1, bit_idx), RTLIL::SigSpec(next_state_onehot, 1, i))); + } + } + else + { + RTLIL::SigSpec sig_a, sig_b, sig_s; + int reset_state = fsm_data.reset_state; + if (reset_state < 0) + reset_state = 0; + + for (size_t i = 0; i < fsm_data.state_table.size(); i++) { + RTLIL::Const state = fsm_data.state_table[i]; + if (int(i) == fsm_data.reset_state) { + sig_a = RTLIL::SigSpec(state); + } else { + sig_b.append(RTLIL::SigSpec(state)); + sig_s.append(RTLIL::SigSpec(next_state_onehot, 1, i)); + } + } + + RTLIL::Cell *mux_cell = new RTLIL::Cell; + mux_cell->name = NEW_ID; + mux_cell->type = "$safe_pmux"; + mux_cell->connections["\\A"] = sig_a; + mux_cell->connections["\\B"] = sig_b; + mux_cell->connections["\\S"] = sig_s; + mux_cell->connections["\\Y"] = RTLIL::SigSpec(next_state_wire); + mux_cell->parameters["\\WIDTH"] = RTLIL::Const(sig_a.width); + mux_cell->parameters["\\S_WIDTH"] = RTLIL::Const(sig_s.width); + module->add(mux_cell); + } + + // Generate ctrl_out signal + + RTLIL::Wire *ctrl_out_wire = new RTLIL::Wire; + ctrl_out_wire->name = NEW_ID; + ctrl_out_wire->width = fsm_data.num_outputs; + module->add(ctrl_out_wire); + + for (int i = 0; i < fsm_data.num_outputs; i++) + { + std::map> pattern_cache; + std::set fullstate_cache; + + for (size_t j = 0; j < fsm_data.state_table.size(); j++) + fullstate_cache.insert(j); + + for (auto &tr : fsm_data.transition_table) { + if (tr.ctrl_out.bits[i] == RTLIL::State::S1) + pattern_cache[tr.ctrl_in].insert(tr.state_in); + else + fullstate_cache.erase(tr.state_in); + } + + implement_pattern_cache(module, pattern_cache, fullstate_cache, fsm_data.state_table.size(), state_onehot, ctrl_in, ctrl_out.extract(i, 1)); + } + + // Remove FSM cell + + module->cells.erase(fsm_cell->name); + delete fsm_cell; +} + +struct FsmMapPass : public Pass { + FsmMapPass() : Pass("fsm_map") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing FSM_MAP pass (simple optimizations of FSMs).\n"); + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) { + std::vector fsm_cells; + for (auto &cell_it : mod_it.second->cells) + if (cell_it.second->type == "$fsm") + fsm_cells.push_back(cell_it.second); + for (auto cell : fsm_cells) + map_fsm(cell, mod_it.second); + } + } +} FsmMapPass; + diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc new file mode 100644 index 000000000..8ba9679fd --- /dev/null +++ b/passes/fsm/fsm_opt.cc @@ -0,0 +1,285 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" +#include + +struct FsmOpt +{ + FsmData fsm_data; + RTLIL::Cell *cell; + RTLIL::Module *module; + + bool signal_is_unused(RTLIL::SigSpec sig) + { + assert(sig.width == 1); + sig.optimize(); + + RTLIL::Wire *wire = sig.chunks[0].wire; + int bit = sig.chunks[0].offset; + + if (!wire || wire->attributes.count("\\unused_bits") == 0) + return false; + + char *str = strdup(wire->attributes["\\unused_bits"].str.c_str()); + for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) { + if (tok[0] && bit == atoi(tok)) + return true; + } + free(str); + + return false; + } + + void opt_const_and_unused_inputs() + { + RTLIL::SigSpec ctrl_in = cell->connections["\\CTRL_IN"]; + std::vector ctrl_in_used(ctrl_in.width); + + std::vector new_transition_table; + for (auto &tr : fsm_data.transition_table) { + for (int i = 0; i < ctrl_in.width; i++) { + RTLIL::SigSpec ctrl_bit = ctrl_in.extract(i, 1); + if (ctrl_bit.is_fully_const()) { + if (tr.ctrl_in.bits[i] <= RTLIL::State::S1 && RTLIL::SigSpec(tr.ctrl_in.bits[i]) != ctrl_bit) + goto delete_this_transition; + continue; + } + if (tr.ctrl_in.bits[i] <= RTLIL::State::S1) + ctrl_in_used[i] = true; + } + new_transition_table.push_back(tr); + delete_this_transition:; + } + + for (int i = int(ctrl_in_used.size())-1; i >= 0; i--) { + if (!ctrl_in_used[i]) { + log(" Removing unused input signal %s.\n", log_signal(cell->connections["\\CTRL_IN"].extract(i, 1))); + for (auto &tr : new_transition_table) { + RTLIL::SigSpec tmp(tr.ctrl_in); + tmp.remove(i, 1); + tr.ctrl_in = tmp.as_const(); + } + cell->connections["\\CTRL_IN"].remove(i, 1); + fsm_data.num_inputs--; + } + } + + fsm_data.transition_table.swap(new_transition_table); + new_transition_table.clear(); + } + + void opt_unused_outputs() + { + for (int i = 0; i < fsm_data.num_outputs; i++) { + RTLIL::SigSpec sig = cell->connections["\\CTRL_OUT"].extract(i, 1); + if (signal_is_unused(sig)) { + log(" Removing unused output signal %s.\n", log_signal(sig)); + cell->connections["\\CTRL_OUT"].remove(i, 1); + for (auto &tr : fsm_data.transition_table) { + RTLIL::SigSpec tmp(tr.ctrl_out); + tmp.remove(i, 1); + tr.ctrl_out = tmp.as_const(); + } + fsm_data.num_outputs--; + i--; + } + } + } + + void opt_alias_inputs() + { + RTLIL::SigSpec &ctrl_in = cell->connections["\\CTRL_IN"]; + + for (int i = 0; i < ctrl_in.width; i++) + for (int j = i+1; j < ctrl_in.width; j++) + if (ctrl_in.extract(i, 1) == ctrl_in.extract(j, 1)) + { + log(" Optimize handling of signal %s that is connected to inputs %d and %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j); + std::vector new_transition_table; + + for (auto tr : fsm_data.transition_table) + { + RTLIL::State &si = tr.ctrl_in.bits[i]; + RTLIL::State &sj = tr.ctrl_in.bits[j]; + + if (si > RTLIL::State::S1) + si = sj; + else if (sj > RTLIL::State::S1) + sj = si; + + if (si == sj) { + RTLIL::SigSpec tmp(tr.ctrl_in); + tmp.remove(j, 1); + tr.ctrl_in = tmp.as_const(); + new_transition_table.push_back(tr); + } + } + + ctrl_in.remove(j--, 1); + fsm_data.num_inputs--; + + fsm_data.transition_table.swap(new_transition_table); + new_transition_table.clear(); + } + } + + void opt_feedback_inputs() + { + RTLIL::SigSpec &ctrl_in = cell->connections["\\CTRL_IN"]; + RTLIL::SigSpec &ctrl_out = cell->connections["\\CTRL_OUT"]; + + for (int j = 0; j < ctrl_out.width; j++) + for (int i = 0; i < ctrl_in.width; i++) + if (ctrl_in.extract(i, 1) == ctrl_out.extract(j, 1)) + { + log(" Optimize handling of signal %s that is connected to input %d and output %d.\n", log_signal(ctrl_in.extract(i, 1)), i, j); + std::vector new_transition_table; + + for (auto tr : fsm_data.transition_table) + { + RTLIL::State &si = tr.ctrl_in.bits[i]; + RTLIL::State &sj = tr.ctrl_out.bits[j]; + + if (si > RTLIL::State::S1 || si == sj) { + RTLIL::SigSpec tmp(tr.ctrl_in); + tmp.remove(i, 1); + tr.ctrl_in = tmp.as_const(); + new_transition_table.push_back(tr); + } + } + + ctrl_in.remove(i--, 1); + fsm_data.num_inputs--; + + fsm_data.transition_table.swap(new_transition_table); + new_transition_table.clear(); + } + } + + void opt_find_dont_care_worker(std::set &set, int bit, FsmData::transition_t &tr, bool &did_something) + { + std::set new_set; + + for (auto &pattern : set) + { + if (pattern.bits[bit] > RTLIL::State::S1) { + new_set.insert(pattern); + continue; + } + + RTLIL::Const other_pattern = pattern; + + if (pattern.bits[bit] == RTLIL::State::S1) + other_pattern.bits[bit] = RTLIL::State::S0; + else + other_pattern.bits[bit] = RTLIL::State::S1; + + if (set.count(other_pattern) > 0) { + log(" Merging pattern %s and %s from group (%d %d %s).\n", log_signal(pattern), log_signal(other_pattern), + tr.state_in, tr.state_out, log_signal(tr.ctrl_out)); + other_pattern.bits[bit] = RTLIL::State::Sa; + new_set.insert(other_pattern); + did_something = true; + continue; + } + + new_set.insert(pattern); + } + + set.swap(new_set); + } + + void opt_find_dont_care() + { + typedef std::pair, RTLIL::Const> group_t; + std::map> transitions_by_group; + + for (auto &tr : fsm_data.transition_table) { + group_t group(std::pair(tr.state_in, tr.state_out), tr.ctrl_out); + transitions_by_group[group].insert(tr.ctrl_in); + } + + fsm_data.transition_table.clear(); + for (auto &it : transitions_by_group) + { + FsmData::transition_t tr; + tr.state_in = it.first.first.first; + tr.state_out = it.first.first.second; + tr.ctrl_out = it.first.second; + + bool did_something = true; + while (did_something) { + did_something = false; + for (int i = 0; i < fsm_data.num_inputs; i++) + opt_find_dont_care_worker(it.second, i, tr, did_something); + } + + for (auto &ci : it.second) { + tr.ctrl_in = ci; + fsm_data.transition_table.push_back(tr); + } + } + } + + FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module) + { + log("Optimizing FSM `%s' from module `%s'.\n", cell->name.c_str(), module->name.c_str()); + + fsm_data.copy_from_cell(cell); + this->cell = cell; + this->module = module; + + opt_unused_outputs(); + + opt_alias_inputs(); + opt_feedback_inputs(); + opt_find_dont_care(); + + opt_const_and_unused_inputs(); + + fsm_data.copy_to_cell(cell); + } +}; + +void FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module) +{ + FsmOpt fsmopt(cell, module); +} + +struct FsmOptPass : public Pass { + FsmOptPass() : Pass("fsm_opt") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n"); + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + for (auto &cell_it : mod_it.second->cells) { + if (cell_it.second->type == "$fsm") + FsmData::optimize_fsm(cell_it.second, mod_it.second); + } + } +} FsmOptPass; + diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc new file mode 100644 index 000000000..5e258f269 --- /dev/null +++ b/passes/fsm/fsm_recode.cc @@ -0,0 +1,114 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/celltypes.h" +#include "fsmdata.h" +#include + +static void fm_set_fsm_print(RTLIL::Cell *cell, RTLIL::Module *module, FsmData &fsm_data, const char *prefix, FILE *f) +{ + fprintf(f, "set_fsm_state_vector {"); + for (int i = fsm_data.state_bits-1; i >= 0; i--) + fprintf(f, " %s_reg[%d]", cell->parameters["\\NAME"].str[0] == '\\' ? + cell->parameters["\\NAME"].str.substr(1).c_str() : cell->parameters["\\NAME"].str.c_str(), i); + fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n", + prefix, RTLIL::unescape_id(cell->parameters["\\NAME"].str).c_str(), + prefix, RTLIL::unescape_id(module->name).c_str()); + + fprintf(f, "set_fsm_encoding {"); + for (size_t i = 0; i < fsm_data.state_table.size(); i++) { + fprintf(f, " s%zd=2#", i); + for (int j = int(fsm_data.state_table[i].bits.size())-1; j >= 0; j--) + fprintf(f, "%c", fsm_data.state_table[i].bits[j] == RTLIL::State::S1 ? '1' : '0'); + } + fprintf(f, " } -name {%s_%s} {%s:/WORK/%s}\n", + prefix, RTLIL::unescape_id(cell->parameters["\\NAME"].str).c_str(), + prefix, RTLIL::unescape_id(module->name).c_str()); +} + +static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fsm_file) +{ + FsmData fsm_data; + fsm_data.copy_from_cell(cell); + + log("Recoding FSM `%s' from module `%s':\n", cell->name.c_str(), module->name.c_str()); + + if (fm_set_fsm_file != NULL) + fm_set_fsm_print(cell, module, fsm_data, "r", fm_set_fsm_file); + + fsm_data.state_bits = fsm_data.state_table.size(); + if (fsm_data.reset_state >= 0) + fsm_data.state_bits--; + + int bit_pos = 0; + for (size_t i = 0; i < fsm_data.state_table.size(); i++) + { + RTLIL::Const new_code; + if (int(i) == fsm_data.reset_state) + new_code = RTLIL::Const(RTLIL::State::S0, fsm_data.state_bits); + else { + RTLIL::Const state_code(RTLIL::State::Sa, fsm_data.state_bits); + state_code.bits[bit_pos++] = RTLIL::State::S1; + new_code = state_code; + } + + log(" %s -> %s\n", fsm_data.state_table[i].as_string().c_str(), new_code.as_string().c_str()); + fsm_data.state_table[i] = new_code; + } + + if (fm_set_fsm_file != NULL) + fm_set_fsm_print(cell, module, fsm_data, "i", fm_set_fsm_file); + + fsm_data.copy_to_cell(cell); +} + +struct FsmRecodePass : public Pass { + FsmRecodePass() : Pass("fsm_recode") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + FILE *fm_set_fsm_file = NULL; + + log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-fm_set_fsm_file" && argidx+1 < args.size() && fm_set_fsm_file == NULL) { + fm_set_fsm_file = fopen(args[++argidx].c_str(), "w"); + if (fm_set_fsm_file == NULL) + log_error("Can't open fm_set_fsm_file `%s' for writing: %s\n", args[argidx].c_str(), strerror(errno)); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto &mod_it : design->modules) + for (auto &cell_it : mod_it.second->cells) + if (cell_it.second->type == "$fsm") + fsm_recode(cell_it.second, mod_it.second, fm_set_fsm_file); + + if (fm_set_fsm_file != NULL) + fclose(fm_set_fsm_file); + } +} FsmRecodePass; + diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h new file mode 100644 index 000000000..f43b25fe9 --- /dev/null +++ b/passes/fsm/fsmdata.h @@ -0,0 +1,177 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef FSMDATA_H +#define FSMDATA_H + +#include "kernel/rtlil.h" +#include "kernel/log.h" + +struct FsmData +{ + int num_inputs, num_outputs, state_bits, reset_state; + struct transition_t { int state_in, state_out; RTLIL::Const ctrl_in, ctrl_out; }; + std::vector transition_table; + std::vector state_table; + + void copy_to_cell(RTLIL::Cell *cell) + { + cell->parameters["\\CTRL_IN_WIDTH"] = RTLIL::Const(num_inputs); + cell->parameters["\\CTRL_OUT_WIDTH"] = RTLIL::Const(num_outputs); + + int state_num_log2 = 0; + for (int i = state_table.size(); i > 0; i = i >> 1) + state_num_log2++; + state_num_log2 = std::max(state_num_log2, 1); + + cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits); + cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size()); + cell->parameters["\\STATE_NUM_LOG2"] = RTLIL::Const(state_num_log2); + cell->parameters["\\STATE_RST"] = RTLIL::Const(reset_state); + cell->parameters["\\STATE_TABLE"] = RTLIL::Const(); + + for (int i = 0; i < int(state_table.size()); i++) { + std::vector &bits_table = cell->parameters["\\STATE_TABLE"].bits; + std::vector &bits_state = state_table[i].bits; + bits_table.insert(bits_table.end(), bits_state.begin(), bits_state.end()); + } + + cell->parameters["\\TRANS_NUM"] = RTLIL::Const(transition_table.size()); + cell->parameters["\\TRANS_TABLE"] = RTLIL::Const(); + for (int i = 0; i < int(transition_table.size()); i++) + { + std::vector &bits_table = cell->parameters["\\TRANS_TABLE"].bits; + transition_t &tr = transition_table[i]; + + RTLIL::Const const_state_in = RTLIL::Const(tr.state_in, state_num_log2); + RTLIL::Const const_state_out = RTLIL::Const(tr.state_out, state_num_log2); + std::vector &bits_state_in = const_state_in.bits; + std::vector &bits_state_out = const_state_out.bits; + + std::vector &bits_ctrl_in = tr.ctrl_in.bits; + std::vector &bits_ctrl_out = tr.ctrl_out.bits; + + // append lsb first + bits_table.insert(bits_table.end(), bits_ctrl_out.begin(), bits_ctrl_out.end()); + bits_table.insert(bits_table.end(), bits_state_out.begin(), bits_state_out.end()); + bits_table.insert(bits_table.end(), bits_ctrl_in.begin(), bits_ctrl_in.end()); + bits_table.insert(bits_table.end(), bits_state_in.begin(), bits_state_in.end()); + } + } + + void copy_from_cell(RTLIL::Cell *cell) + { + num_inputs = cell->parameters["\\CTRL_IN_WIDTH"].as_int(); + num_outputs = cell->parameters["\\CTRL_OUT_WIDTH"].as_int(); + + state_bits = cell->parameters["\\STATE_BITS"].as_int(); + reset_state = cell->parameters["\\STATE_RST"].as_int(); + + int state_num = cell->parameters["\\STATE_NUM"].as_int(); + int state_num_log2 = cell->parameters["\\STATE_NUM_LOG2"].as_int(); + int trans_num = cell->parameters["\\TRANS_NUM"].as_int(); + + if (reset_state < 0 || reset_state >= state_num) + reset_state = -1; + + RTLIL::Const state_table = cell->parameters["\\STATE_TABLE"]; + RTLIL::Const trans_table = cell->parameters["\\TRANS_TABLE"]; + + for (int i = 0; i < state_num; i++) { + RTLIL::Const state_code; + int off_begin = i*state_bits, off_end = off_begin + state_bits; + state_code.bits.insert(state_code.bits.begin(), state_table.bits.begin()+off_begin, state_table.bits.begin()+off_end); + this->state_table.push_back(state_code); + } + + for (int i = 0; i < trans_num; i++) + { + auto off_ctrl_out = trans_table.bits.begin() + i*(num_inputs+num_outputs+2*state_num_log2); + auto off_state_out = off_ctrl_out + num_outputs; + auto off_ctrl_in = off_state_out + state_num_log2; + auto off_state_in = off_ctrl_in + num_inputs; + auto off_end = off_state_in + state_num_log2; + + RTLIL::Const state_in, state_out, ctrl_in, ctrl_out; + ctrl_out.bits.insert(state_in.bits.begin(), off_ctrl_out, off_state_out); + state_out.bits.insert(state_out.bits.begin(), off_state_out, off_ctrl_in); + ctrl_in.bits.insert(ctrl_in.bits.begin(), off_ctrl_in, off_state_in); + state_in.bits.insert(state_in.bits.begin(), off_state_in, off_end); + + transition_t tr; + tr.state_in = state_in.as_int(); + tr.state_out = state_out.as_int(); + tr.ctrl_in = ctrl_in; + tr.ctrl_out = ctrl_out; + + if (tr.state_in < 0 || tr.state_in >= state_num) + tr.state_in = -1; + if (tr.state_out < 0 || tr.state_out >= state_num) + tr.state_out = -1; + + transition_table.push_back(tr); + } + } + + void log_info(RTLIL::Cell *cell) + { + log("-------------------------------------\n"); + log("\n"); + log(" Information on FSM %s (%s):\n", cell->name.c_str(), cell->parameters["\\NAME"].str.c_str()); + log("\n"); + log(" Number of input signals: %3d\n", num_inputs); + log(" Number of output signals: %3d\n", num_outputs); + log(" Number of state bits: %3d\n", state_bits); + + log("\n"); + log(" Input signals:\n"); + RTLIL::SigSpec sig_in = cell->connections["\\CTRL_IN"]; + sig_in.expand(); + for (size_t i = 0; i < sig_in.chunks.size(); i++) + log(" %3zd: %s\n", i, log_signal(sig_in.chunks[i])); + + log("\n"); + log(" Output signals:\n"); + RTLIL::SigSpec sig_out = cell->connections["\\CTRL_OUT"]; + sig_out.expand(); + for (size_t i = 0; i < sig_out.chunks.size(); i++) + log(" %3zd: %s\n", i, log_signal(sig_out.chunks[i])); + + log("\n"); + log(" State encoding:\n"); + for (size_t i = 0; i < state_table.size(); i++) + log(" %3zd: %10s%s\n", i, log_signal(state_table[i], false), + int(i) == reset_state ? " " : ""); + + log("\n"); + log(" Transition Table (state_in, ctrl_in, state_out, ctrl_out):\n"); + for (size_t i = 0; i < transition_table.size(); i++) { + transition_t &tr = transition_table[i]; + log(" %5zd: %5d %s -> %5d %s\n", i, tr.state_in, log_signal(tr.ctrl_in), tr.state_out, log_signal(tr.ctrl_out)); + } + + log("\n"); + log("-------------------------------------\n"); + } + + // implemented in fsm_opt.cc + static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module); +}; + +#endif diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc new file mode 100644 index 000000000..37cc287ac --- /dev/null +++ b/passes/hierarchy/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += passes/hierarchy/hierarchy.o + diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc new file mode 100644 index 000000000..c8cd77a16 --- /dev/null +++ b/passes/hierarchy/hierarchy.cc @@ -0,0 +1,194 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include +#include + +static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check) +{ + bool did_something = false; + + for (auto &cell_it : module->cells) { + RTLIL::Cell *cell = cell_it.second; + if (design->modules.count(cell->type) == 0) { + if (flag_check && cell->type[0] != '$') + log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", + cell->type.c_str(), module->name.c_str(), cell->name.c_str()); + continue; + } + if (cell->parameters.size() == 0) + continue; + RTLIL::Module *mod = design->modules[cell->type]; + cell->type = mod->derive(design, cell->parameters); + cell->parameters.clear(); + } + + if (did_something) + return did_something; + + std::map auto_wires; + + for (auto &wire_it : module->wires) { + if (wire_it.second->auto_width) + auto_wires[RTLIL::SigSpec(wire_it.second)] = -1; + } + + for (auto &cell_it : module->cells) + for (auto &conn : cell_it.second->connections) + for (auto &awit : auto_wires) { + if (awit.second >= 0 || conn.second != awit.first) + continue; + if (design->modules.count(cell_it.second->type) == 0) { + log("WARNING: Module `%s' used in auto-delaration of the wire `%s.%s' cannot be found.\n", + cell_it.second->type.c_str(), module->name.c_str(), log_signal(awit.first)); + continue; + } + RTLIL::Module *mod = design->modules[cell_it.second->type]; + RTLIL::Wire *wire = NULL; + if (mod->wires.count(conn.first) == 0) { + for (auto &wire_it : mod->wires) { + if (wire_it.second->port_id == 0) + continue; + char buffer[100]; + snprintf(buffer, 100, "$%d", wire_it.second->port_id); + if (buffer == conn.first) { + wire = wire_it.second; + break; + } + } + } else + wire = mod->wires[conn.first]; + if (!wire || wire->port_id == 0) + log_error("No port `%s' found in `%s' but used by instanciation in `%s'!\n", + conn.first.c_str(), mod->name.c_str(), module->name.c_str()); + if (wire->auto_width) + log_error("Signal `%s' found in `%s' and used by instanciation in `%s' for an auto wire is an auto-wire itself!\n", + log_signal(awit.first), mod->name.c_str(), module->name.c_str()); + awit.second = wire->width; + } + + std::map auto_sizes; + for (auto &awit : auto_wires) { + if (awit.second < 0) + log("Can't further resolve auto-wire `%s.%s' (width %d) using cell ports.\n", + module->name.c_str(), awit.first.chunks[0].wire->name.c_str(), + awit.first.chunks[0].wire->width); + else + auto_sizes[awit.first.chunks[0].wire->name] = awit.second; + } + + if (auto_sizes.size() > 0) { + module->update_auto_wires(auto_sizes); + log_header("Continuing EXPAND pass.\n"); + did_something = true; + } + + return did_something; +} + +static void hierarchy_worker(RTLIL::Design *design, std::set &used, RTLIL::Module *mod, bool is_top = false) +{ + if (used.count(mod) > 0) + return; + + log("%s module: %s\n", is_top ? "Top" : "Used", mod->name.c_str()); + used.insert(mod); + + for (auto &it : mod->cells) { + if (design->modules.count(it.second->type) > 0) + hierarchy_worker(design, used, design->modules[it.second->type]); + } +} + +static void hierarchy(RTLIL::Design *design, RTLIL::Module *top) +{ + std::set used; + hierarchy_worker(design, used, top, true); + + std::vector del_modules; + for (auto &it : design->modules) + if (used.count(it.second) == 0) + del_modules.push_back(it.second); + + for (auto mod : del_modules) { + log("Removing unused module `%s'.\n", mod->name.c_str()); + design->modules.erase(mod->name); + delete mod; + } + + log("Removed %zd unused modules.\n", del_modules.size()); +} + +struct HierarchyPass : public Pass { + HierarchyPass() : Pass("hierarchy") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing HIERARCHY pass (removing modules outside design hierarchy).\n"); + + bool flag_check = false; + RTLIL::Module *top_mod = NULL; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-check") { + flag_check = true; + continue; + } + if (args[argidx] == "-top") { + if (++argidx >= args.size()) + log_cmd_error("Option -top requires an additional argument!\n"); + if (args[argidx][0] != '$' && args[argidx][0] != '\\') + top_mod = design->modules.count("\\" + args[argidx]) > 0 ? design->modules["\\" + args[argidx]] : NULL; + else + top_mod = design->modules.count(args[argidx]) > 0 ? design->modules[args[argidx]] : NULL; + if (top_mod == NULL) + log_cmd_error("Module `%s' not found!\n", args[argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + if (top_mod != NULL) + hierarchy(design, top_mod); + + bool did_something = true; + while (did_something) { + did_something = false; + std::vector modnames; + modnames.reserve(design->modules.size()); + for (auto &mod_it : design->modules) + modnames.push_back(mod_it.first); + for (auto &modname : modnames) { + if (design->modules.count(modname) == 0) + continue; + if (expand_module(design, design->modules[modname], flag_check)) + did_something = true; + } + } + + if (top_mod != NULL) + hierarchy(design, top_mod); + } +} HierarchyPass; + diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc new file mode 100644 index 000000000..cffdefe85 --- /dev/null +++ b/passes/memory/Makefile.inc @@ -0,0 +1,6 @@ + +OBJS += passes/memory/memory.o +OBJS += passes/memory/memory_dff.o +OBJS += passes/memory/memory_collect.o +OBJS += passes/memory/memory_map.o + diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc new file mode 100644 index 000000000..c5533b630 --- /dev/null +++ b/passes/memory/memory.cc @@ -0,0 +1,40 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +struct MemoryPass : public Pass { + MemoryPass() : Pass("memory") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing MEMORY pass.\n"); + log_push(); + + extra_args(args, 1, design); + Pass::call(design, "memory_dff"); + Pass::call(design, "memory_collect"); + Pass::call(design, "memory_map"); + + log_pop(); + } +} MemoryPass; + diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc new file mode 100644 index 000000000..b89b97695 --- /dev/null +++ b/passes/memory/memory_collect.cc @@ -0,0 +1,182 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include +#include + +static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) +{ + log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n", + memory->name.c_str(), module->name.c_str()); + + int addr_bits = 0; + while ((1 << addr_bits) < memory->size) + addr_bits++; + + int wr_ports = 0; + RTLIL::SigSpec sig_wr_clk; + RTLIL::SigSpec sig_wr_clk_enable; + RTLIL::SigSpec sig_wr_clk_polarity; + RTLIL::SigSpec sig_wr_addr; + RTLIL::SigSpec sig_wr_data; + RTLIL::SigSpec sig_wr_en; + + int rd_ports = 0; + RTLIL::SigSpec sig_rd_clk; + RTLIL::SigSpec sig_rd_clk_enable; + RTLIL::SigSpec sig_rd_clk_polarity; + RTLIL::SigSpec sig_rd_addr; + RTLIL::SigSpec sig_rd_data; + + std::vector del_cell_ids; + + for (auto &cell_it : module->cells) + { + RTLIL::Cell *cell = cell_it.second; + + if (cell->type == "$memwr" && cell->parameters["\\MEMID"].str == memory->name) + { + wr_ports++; + del_cell_ids.push_back(cell->name); + + RTLIL::SigSpec clk = cell->connections["\\CLK"]; + RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); + RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); + RTLIL::SigSpec addr = cell->connections["\\ADDR"]; + RTLIL::SigSpec data = cell->connections["\\DATA"]; + RTLIL::SigSpec en = cell->connections["\\EN"]; + + clk.extend(1, false); + clk_enable.extend(1, false); + clk_polarity.extend(1, false); + addr.extend(addr_bits, false); + data.extend(memory->width, false); + en.extend(1, false); + + sig_wr_clk.append(clk); + sig_wr_clk_enable.append(clk_enable); + sig_wr_clk_polarity.append(clk_polarity); + sig_wr_addr.append(addr); + sig_wr_data.append(data); + sig_wr_en.append(en); + } + + if (cell->type == "$memrd" && cell->parameters["\\MEMID"].str == memory->name) + { + rd_ports++; + del_cell_ids.push_back(cell->name); + + RTLIL::SigSpec clk = cell->connections["\\CLK"]; + RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); + RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); + RTLIL::SigSpec addr = cell->connections["\\ADDR"]; + RTLIL::SigSpec data = cell->connections["\\DATA"]; + + clk.extend(1, false); + clk_enable.extend(1, false); + clk_polarity.extend(1, false); + addr.extend(addr_bits, false); + data.extend(memory->width, false); + + sig_rd_clk.append(clk); + sig_rd_clk_enable.append(clk_enable); + sig_rd_clk_polarity.append(clk_polarity); + sig_rd_addr.append(addr); + sig_rd_data.append(data); + } + } + + std::stringstream sstr; + sstr << "$mem$" << memory->name << "$" << (RTLIL::autoidx++); + + RTLIL::Cell *mem = new RTLIL::Cell; + mem->name = sstr.str(); + mem->type = "$mem"; + + mem->parameters["\\MEMID"] = RTLIL::Const(memory->name); + mem->parameters["\\WIDTH"] = RTLIL::Const(memory->width); + mem->parameters["\\OFFSET"] = RTLIL::Const(memory->start_offset); + mem->parameters["\\SIZE"] = RTLIL::Const(memory->size); + mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits); + + sig_wr_clk_enable.optimize(); + sig_wr_clk_polarity.optimize(); + + assert(sig_wr_clk.width == wr_ports); + assert(sig_wr_clk_enable.width == wr_ports && sig_wr_clk_enable.is_fully_const()); + assert(sig_wr_clk_polarity.width == wr_ports && sig_wr_clk_polarity.is_fully_const()); + assert(sig_wr_addr.width == wr_ports * addr_bits); + assert(sig_wr_data.width == wr_ports * memory->width); + assert(sig_wr_en.width == wr_ports); + + mem->parameters["\\WR_PORTS"] = RTLIL::Const(wr_ports); + mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.chunks[0].data : RTLIL::Const(0, 0); + mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_enable.chunks[0].data : RTLIL::Const(0, 0); + + mem->connections["\\WR_CLK"] = sig_wr_clk; + mem->connections["\\WR_ADDR"] = sig_wr_addr; + mem->connections["\\WR_DATA"] = sig_wr_data; + mem->connections["\\WR_EN"] = sig_wr_en; + + sig_rd_clk_enable.optimize(); + sig_rd_clk_polarity.optimize(); + + assert(sig_rd_clk.width == rd_ports); + assert(sig_rd_clk_enable.width == rd_ports && sig_rd_clk_enable.is_fully_const()); + assert(sig_rd_clk_polarity.width == rd_ports && sig_rd_clk_polarity.is_fully_const()); + assert(sig_rd_addr.width == rd_ports * addr_bits); + assert(sig_rd_data.width == rd_ports * memory->width); + + mem->parameters["\\RD_PORTS"] = RTLIL::Const(rd_ports); + mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.chunks[0].data : RTLIL::Const(0, 0); + mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_enable.chunks[0].data : RTLIL::Const(0, 0); + + mem->connections["\\RD_CLK"] = sig_rd_clk; + mem->connections["\\RD_ADDR"] = sig_rd_addr; + mem->connections["\\RD_DATA"] = sig_rd_data; + + for (auto &id : del_cell_ids) { + delete module->cells[id]; + module->cells.erase(id); + } + module->cells[mem->name] = mem; +} + +static void handle_module(RTLIL::Module *module) +{ + for (auto &mem_it : module->memories) { + handle_memory(module, mem_it.second); + delete mem_it.second; + } + module->memories.clear(); +} + +struct MemoryCollectPass : public Pass { + MemoryCollectPass() : Pass("memory_collect") { } + virtual void execute(std::vector args, RTLIL::Design *design) { + log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n"); + extra_args(args, 1, design); + for (auto &mod_it : design->modules) + handle_module(mod_it.second); + } +} MemoryCollectPass; + diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc new file mode 100644 index 000000000..e188a7af8 --- /dev/null +++ b/passes/memory/memory_dff.cc @@ -0,0 +1,200 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include +#include + +static void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig) +{ + for (auto &conn : module->connections) + sig.replace(conn.first, conn.second); +} + +static bool find_sig_before_dff(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) +{ + bool replaced_bits = false; + normalize_sig(module, sig); + sig.expand(); + + for (size_t i = 0; i < sig.chunks.size(); i++) + { + RTLIL::SigChunk &chunk = sig.chunks[i]; + + if (chunk.wire == NULL) + continue; + + for (auto &cell_it : module->cells) + { + RTLIL::Cell *cell = cell_it.second; + + if (cell->type != "$dff") + continue; + + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (cell->connections["\\CLK"] != clk) + continue; + if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity) + continue; + } + + RTLIL::SigSpec q_norm = cell->connections[after ? "\\D" : "\\Q"]; + normalize_sig(module, q_norm); + + RTLIL::SigSpec d = q_norm.extract(chunk, &cell->connections[after ? "\\Q" : "\\D"]); + if (d.width != 1) + continue; + + assert(d.chunks.size() == 1); + chunk = d.chunks[0]; + clk = cell->connections["\\CLK"]; + clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); + replaced_bits = true; + goto replaced_this_bit; + } + + return false; + replaced_this_bit:; + } + + sig.optimize(); + return replaced_bits; +} + +static void handle_wr_cell(RTLIL::Module *module, RTLIL::Cell *cell) +{ + log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + + RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); + bool clk_polarity = 0; + + RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"]; + if (!find_sig_before_dff(module, sig_addr, clk, clk_polarity)) { + log("no (compatible) $dff for address input found.\n"); + return; + } + + RTLIL::SigSpec sig_data = cell->connections["\\DATA"]; + if (!find_sig_before_dff(module, sig_data, clk, clk_polarity)) { + log("no (compatible) $dff for data input found.\n"); + return; + } + + RTLIL::SigSpec sig_en = cell->connections["\\EN"]; + if (!find_sig_before_dff(module, sig_en, clk, clk_polarity)) { + log("no (compatible) $dff for enable input found.\n"); + return; + } + + cell->connections["\\CLK"] = clk; + cell->connections["\\ADDR"] = sig_addr; + cell->connections["\\DATA"] = sig_data; + cell->connections["\\EN"] = sig_en; + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + log("merged $dff to cell.\n"); +} + +#if 1 +static void handle_rd_cell(RTLIL::Module*, RTLIL::Cell*) +{ + // merging dffs into read ports isn't neccessary for memory_map. + // we'd loose the information if the register is on the address or + // data port and wouldn't get any benefits. +} +#else +static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) +{ + normalize_sig(module, sig); + sig.sort_and_unify(); + + std::stringstream sstr; + sstr << "$memory_dff_disconnected$" << (RTLIL::autoidx++); + + RTLIL::Wire *wire = new RTLIL::Wire; + wire->name = sstr.str(); + wire->width = sig.width; + module->wires[wire->name] = wire; + + RTLIL::SigSpec newsig(wire); + + for (auto &cell_it : module->cells) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$dff") + cell->connections["\\Q"].replace(sig, newsig); + } +} + +static void handle_rd_cell(RTLIL::Module *module, RTLIL::Cell *cell) +{ + log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + + bool clk_polarity = 0; + + RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec sig_addr = cell->connections["\\ADDR"]; + if (find_sig_before_dff(module, sig_addr, clk_addr, clk_polarity)) + { + cell->connections["\\CLK"] = clk_addr; + cell->connections["\\ADDR"] = sig_addr; + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + log("merged address $dff to cell.\n"); + return; + } + + RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec sig_data = cell->connections["\\DATA"]; + if (find_sig_before_dff(module, sig_data, clk_data, clk_polarity, true)) + { + disconnect_dff(module, sig_data); + cell->connections["\\CLK"] = clk_data; + cell->connections["\\DATA"] = sig_data; + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + log("merged data $dff to cell.\n"); + return; + } + + log("no (compatible) $dff found.\n"); +} +#endif + +static void handle_module(RTLIL::Module *module) +{ + for (auto &cell_it : module->cells) { + if (cell_it.second->type == "$memwr" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool()) + handle_wr_cell(module, cell_it.second); + if (cell_it.second->type == "$memrd" && !cell_it.second->parameters["\\CLK_ENABLE"].as_bool()) + handle_rd_cell(module, cell_it.second); + } +} + +struct MemoryDffPass : public Pass { + MemoryDffPass() : Pass("memory_dff") { } + virtual void execute(std::vector args, RTLIL::Design *design) { + log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); + extra_args(args, 1, design); + for (auto &mod_it : design->modules) + handle_module(mod_it.second); + } +} MemoryDffPass; + diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc new file mode 100644 index 000000000..e7783083b --- /dev/null +++ b/passes/memory/memory_map.cc @@ -0,0 +1,334 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include +#include +#include + +static std::string genid(std::string name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "") +{ + std::stringstream sstr; + sstr << "$memory" << name << token1; + + if (i >= 0) + sstr << "[" << i << "]"; + + sstr << token2; + + if (j >= 0) + sstr << "[" << j << "]"; + + sstr << token3; + + if (k >= 0) + sstr << "[" << k << "]"; + + sstr << token4 << "$" << (RTLIL::autoidx++); + return sstr.str(); +} + +static void handle_cell(RTLIL::Module *module, RTLIL::Cell *cell) +{ + std::set static_ports; + std::map static_cells_map; + int mem_size = cell->parameters["\\SIZE"].as_int(); + int mem_width = cell->parameters["\\WIDTH"].as_int(); + int mem_offset = cell->parameters["\\OFFSET"].as_int(); + int mem_abits = cell->parameters["\\ABITS"].as_int(); + + // delete unused memory cell + if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) { + module->cells.erase(cell->name); + delete cell; + return; + } + + // all write ports must share the same clock + RTLIL::SigSpec clocks = cell->connections["\\WR_CLK"]; + RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"]; + RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"]; + RTLIL::SigSpec refclock; + RTLIL::State refclock_pol = RTLIL::State::Sx; + for (int i = 0; i < clocks.width; i++) { + RTLIL::SigSpec wr_en = cell->connections["\\WR_EN"].extract(i, 1); + if (wr_en.is_fully_const() && wr_en.as_int() == 0) { + static_ports.insert(i); + continue; + } + if (clocks_en.bits[i] != RTLIL::State::S1) { + RTLIL::SigSpec wr_addr = cell->connections["\\WR_ADDR"].extract(i*mem_abits, mem_abits); + RTLIL::SigSpec wr_data = cell->connections["\\WR_DATA"].extract(i*mem_width, mem_width); + if (wr_addr.is_fully_const()) { + // FIXME: Actually we should check for wr_en.is_fully_const() also and + // create a $adff cell with this ports wr_en input as reset pin when wr_en + // is not a simple static 1. + static_cells_map[wr_addr.as_int()] = wr_data; + static_ports.insert(i); + continue; + } + log("Not mapping memory cell %s in module %s (write port %d has no clock).\n", + cell->name.c_str(), module->name.c_str(), i); + return; + } + if (refclock.width == 0) { + refclock = clocks.extract(i, 1); + refclock_pol = clocks_pol.bits[i]; + } + if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) { + log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n", + cell->name.c_str(), module->name.c_str(), i); + return; + } + } + + log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str()); + + std::vector data_reg_in; + std::vector data_reg_out; + + int count_static = 0; + + for (int i = 0; i < mem_size; i++) + { + if (static_cells_map.count(i) > 0) + { + data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width)); + data_reg_out.push_back(static_cells_map[i]); + count_static++; + } + else + { + RTLIL::Cell *c = new RTLIL::Cell; + c->name = genid(cell->name, "", i); + c->type = "$dff"; + c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(clocks_pol.bits[0]); + c->connections["\\CLK"] = clocks.extract(0, 1); + module->cells[c->name] = c; + + RTLIL::Wire *w_in = new RTLIL::Wire; + w_in->name = genid(cell->name, "", i, "$d"); + w_in->width = mem_width; + module->wires[w_in->name] = w_in; + data_reg_in.push_back(RTLIL::SigSpec(w_in)); + c->connections["\\D"] = data_reg_in.back(); + + RTLIL::Wire *w_out = new RTLIL::Wire; + w_out->name = stringf("%s[%d]", cell->parameters["\\MEMID"].str.c_str(), i); + if (module->wires.count(w_out->name) > 0) + w_out->name = genid(cell->name, "", i, "$q"); + w_out->width = mem_width; + w_out->start_offset = mem_offset; + module->wires[w_out->name] = w_out; + data_reg_out.push_back(RTLIL::SigSpec(w_out)); + c->connections["\\Q"] = data_reg_out.back(); + } + } + + log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width); + + int count_dff = 0, count_mux = 0, count_wrmux = 0; + + for (int i = 0; i < cell->parameters["\\RD_PORTS"].as_int(); i++) + { + RTLIL::SigSpec rd_addr = cell->connections["\\RD_ADDR"].extract(i*mem_abits, mem_abits); + + std::vector rd_signals; + rd_signals.push_back(cell->connections["\\RD_DATA"].extract(i*mem_width, mem_width)); + + if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1) + { +#if 1 + RTLIL::Cell *c = new RTLIL::Cell; + c->name = genid(cell->name, "$rdreg", i); + c->type = "$dff"; + c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + c->connections["\\CLK"] = cell->connections["\\RD_CLK"].extract(i, 1); + c->connections["\\D"] = rd_addr; + module->cells[c->name] = c; + count_dff++; + + RTLIL::Wire *w = new RTLIL::Wire; + w->name = genid(cell->name, "$rdreg", i, "$q"); + w->width = mem_abits; + module->wires[w->name] = w; + + c->connections["\\Q"] = RTLIL::SigSpec(w); + rd_addr = RTLIL::SigSpec(w); +#else + RTLIL::Cell *c = new RTLIL::Cell; + c->name = genid(cell->name, "$rdreg", i); + c->type = "$dff"; + c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + c->connections["\\CLK"] = cell->connections["\\RD_CLK"].extract(i, 1); + c->connections["\\Q"] = rd_signals.back(); + module->cells[c->name] = c; + count_dff++; + + RTLIL::Wire *w = new RTLIL::Wire; + w->name = genid(cell->name, "$rdreg", i, "$d"); + w->width = mem_width; + module->wires[w->name] = w; + + rd_signals.clear(); + rd_signals.push_back(RTLIL::SigSpec(w)); + c->connections["\\D"] = rd_signals.back(); +#endif + } + + for (int j = 0; j < mem_abits; j++) + { + std::vector next_rd_signals; + + for (size_t k = 0; k < rd_signals.size(); k++) + { + RTLIL::Cell *c = new RTLIL::Cell; + c->name = genid(cell->name, "$rdmux", i, "", j, "", k); + c->type = "$mux"; + c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + c->connections["\\Y"] = rd_signals[k]; + c->connections["\\S"] = rd_addr.extract(mem_abits-j-1, 1); + module->cells[c->name] = c; + count_mux++; + + RTLIL::Wire *w = new RTLIL::Wire; + w->name = genid(cell->name, "$rdmux", i, "", j, "", k, "$a"); + w->width = mem_width; + module->wires[w->name] = w; + c->connections["\\A"] = RTLIL::SigSpec(w); + + w = new RTLIL::Wire; + w->name = genid(cell->name, "$rdmux", i, "", j, "", k, "$b"); + w->width = mem_width; + module->wires[w->name] = w; + c->connections["\\B"] = RTLIL::SigSpec(w); + + next_rd_signals.push_back(c->connections["\\A"]); + next_rd_signals.push_back(c->connections["\\B"]); + } + + next_rd_signals.swap(rd_signals); + } + + for (int j = 0; j < mem_size; j++) + module->connections.push_back(RTLIL::SigSig(rd_signals[j], data_reg_out[j])); + } + + log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux); + + for (int i = 0; i < mem_size; i++) + { + if (static_cells_map.count(i) > 0) + continue; + + RTLIL::SigSpec sig = data_reg_out[i]; + + for (int j = 0; j < cell->parameters["\\WR_PORTS"].as_int(); j++) + { + RTLIL::SigSpec wr_addr = cell->connections["\\WR_ADDR"].extract(j*mem_abits, mem_abits); + RTLIL::SigSpec wr_data = cell->connections["\\WR_DATA"].extract(j*mem_width, mem_width); + RTLIL::SigSpec wr_en = cell->connections["\\WR_EN"].extract(j, 1); + + RTLIL::Cell *c = new RTLIL::Cell; + c->name = genid(cell->name, "$wreq", i, "", j); + c->type = "$eq"; + c->parameters["\\A_SIGNED"] = RTLIL::Const(0); + c->parameters["\\B_SIGNED"] = RTLIL::Const(0); + c->parameters["\\A_WIDTH"] = cell->parameters["\\ABITS"]; + c->parameters["\\B_WIDTH"] = cell->parameters["\\ABITS"]; + c->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + c->connections["\\A"] = RTLIL::SigSpec(i, mem_abits); + c->connections["\\B"] = wr_addr; + module->cells[c->name] = c; + count_wrmux++; + + RTLIL::Wire *w = new RTLIL::Wire; + w->name = genid(cell->name, "$wreq", i, "", j, "$y"); + module->wires[w->name] = w; + c->connections["\\Y"] = RTLIL::SigSpec(w); + + c = new RTLIL::Cell; + c->name = genid(cell->name, "$wren", i, "", j); + c->type = "$and"; + c->parameters["\\A_SIGNED"] = RTLIL::Const(0); + c->parameters["\\B_SIGNED"] = RTLIL::Const(0); + c->parameters["\\A_WIDTH"] = RTLIL::Const(1); + c->parameters["\\B_WIDTH"] = RTLIL::Const(1); + c->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + c->connections["\\A"] = RTLIL::SigSpec(w); + c->connections["\\B"] = wr_en; + module->cells[c->name] = c; + + w = new RTLIL::Wire; + w->name = genid(cell->name, "$wren", i, "", j, "$y"); + module->wires[w->name] = w; + c->connections["\\Y"] = RTLIL::SigSpec(w); + + c = new RTLIL::Cell; + c->name = genid(cell->name, "$wrmux", i, "", j); + c->type = "$mux"; + c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + c->connections["\\A"] = sig; + c->connections["\\B"] = wr_data; + c->connections["\\S"] = RTLIL::SigSpec(w); + module->cells[c->name] = c; + + w = new RTLIL::Wire; + w->name = genid(cell->name, "$wrmux", i, "", j, "$y"); + w->width = mem_width; + module->wires[w->name] = w; + c->connections["\\Y"] = RTLIL::SigSpec(w); + sig = RTLIL::SigSpec(w); + } + + module->connections.push_back(RTLIL::SigSig(data_reg_in[i], sig)); + } + + log(" write interface: %d blocks of $eq, $and and $mux cells.\n", count_wrmux); + + module->cells.erase(cell->name); + delete cell; + return; +} + +static void handle_module(RTLIL::Module *module) +{ + std::vector cells; + for (auto &it : module->cells) + if (it.second->type == "$mem") + cells.push_back(it.second); + for (auto cell : cells) + handle_cell(module, cell); +} + +struct MemoryMapPass : public Pass { + MemoryMapPass() : Pass("memory_map") { } + virtual void execute(std::vector args, RTLIL::Design *design) { + log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); + extra_args(args, 1, design); + for (auto &mod_it : design->modules) + handle_module(mod_it.second); + } +} MemoryMapPass; + diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc new file mode 100644 index 000000000..3602b96bd --- /dev/null +++ b/passes/opt/Makefile.inc @@ -0,0 +1,9 @@ + +OBJS += passes/opt/opt.o +OBJS += passes/opt/opt_share.o +OBJS += passes/opt/opt_muxtree.o +OBJS += passes/opt/opt_reduce.o +OBJS += passes/opt/opt_rmdff.o +OBJS += passes/opt/opt_rmunused.o +OBJS += passes/opt/opt_const.o + diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc new file mode 100644 index 000000000..9e33f78ca --- /dev/null +++ b/passes/opt/opt.cc @@ -0,0 +1,62 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +bool OPT_DID_SOMETHING; + +struct OptPass : public Pass { + OptPass() : Pass("opt") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing OPT pass (performing simple optimizations).\n"); + log_push(); + + extra_args(args, 1, design); + + log_header("Optimizing in-memory representation of design.\n"); + design->optimize(); + + Pass::call(design, "opt_const"); + Pass::call(design, "opt_share -nomux"); + while (1) { + OPT_DID_SOMETHING = false; + Pass::call(design, "opt_muxtree"); + Pass::call(design, "opt_reduce"); + Pass::call(design, "opt_share"); + Pass::call(design, "opt_rmdff"); + Pass::call(design, "opt_rmunused"); + Pass::call(design, "opt_const"); + if (OPT_DID_SOMETHING == false) + break; + log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + } + + log_header("Optimizing in-memory representation of design.\n"); + design->optimize(); + + log_header("Finished OPT passes. (There is nothing left to do.)\n"); + log_pop(); + } +} OptPass; + diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc new file mode 100644 index 000000000..9edea6709 --- /dev/null +++ b/passes/opt/opt_const.cc @@ -0,0 +1,263 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#undef MUX_UNDEF_SEL_TO_UNDEF_RESULTS + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include +#include +#include +#include + +bool did_something; + +void replace_cell(RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) +{ + RTLIL::SigSpec Y = cell->connections[out_port]; + log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n", + cell->type.c_str(), cell->name.c_str(), info.c_str(), + module->name.c_str(), log_signal(Y), log_signal(out_val)); + OPT_DID_SOMETHING = true; + // ILANG_BACKEND::dump_cell(stderr, "--> ", cell); + module->connections.push_back(RTLIL::SigSig(Y, out_val)); + module->cells.erase(cell->name); + delete cell; + did_something = true; +} + +void replace_const_cells(RTLIL::Module *module) +{ + SigMap assign_map(module); + + std::vector cells; + cells.reserve(module->cells.size()); + for (auto &cell_it : module->cells) + cells.push_back(cell_it.second); + + for (auto cell : cells) + { +#define ACTION_DO(_p_, _s_) do { replace_cell(module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) + + if (cell->type == "$_INV_") { + RTLIL::SigSpec input = cell->connections["\\A"]; + assign_map.apply(input); + if (input.match("1")) ACTION_DO_Y(0); + if (input.match("0")) ACTION_DO_Y(1); + if (input.match("*")) ACTION_DO_Y(x); + } + + if (cell->type == "$_AND_") { + RTLIL::SigSpec input; + input.append(cell->connections["\\B"]); + input.append(cell->connections["\\A"]); + assign_map.apply(input); + if (input.match(" 0")) ACTION_DO_Y(0); + if (input.match("0 ")) ACTION_DO_Y(0); + if (input.match("11")) ACTION_DO_Y(1); + if (input.match(" *")) ACTION_DO_Y(x); + if (input.match("* ")) ACTION_DO_Y(x); + } + + if (cell->type == "$_OR_") { + RTLIL::SigSpec input; + input.append(cell->connections["\\B"]); + input.append(cell->connections["\\A"]); + assign_map.apply(input); + if (input.match(" 1")) ACTION_DO_Y(1); + if (input.match("1 ")) ACTION_DO_Y(1); + if (input.match("00")) ACTION_DO_Y(0); + if (input.match(" *")) ACTION_DO_Y(x); + if (input.match("* ")) ACTION_DO_Y(x); + } + + if (cell->type == "$_XOR_") { + RTLIL::SigSpec input; + input.append(cell->connections["\\B"]); + input.append(cell->connections["\\A"]); + assign_map.apply(input); + if (input.match("00")) ACTION_DO_Y(0); + if (input.match("01")) ACTION_DO_Y(1); + if (input.match("10")) ACTION_DO_Y(1); + if (input.match("11")) ACTION_DO_Y(0); + if (input.match(" *")) ACTION_DO_Y(x); + if (input.match("* ")) ACTION_DO_Y(x); + } + + if (cell->type == "$_MUX_") { + RTLIL::SigSpec input; + input.append(cell->connections["\\S"]); + input.append(cell->connections["\\B"]); + input.append(cell->connections["\\A"]); + assign_map.apply(input); + if (input.extract(2, 1) == input.extract(1, 1)) + ACTION_DO("\\Y", input.extract(2, 1)); + if (input.match(" 0")) ACTION_DO("\\Y", input.extract(2, 1)); + if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); +#ifdef MUX_UNDEF_SEL_TO_UNDEF_RESULTS + if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); + if (input.match(" *")) ACTION_DO_Y(x); +#endif + } + + if (cell->type == "$eq" || cell->type == "$ne") + { + if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { + int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); + cell->connections["\\A"].extend(width, cell->parameters["\\A_SIGNED"].as_bool()); + cell->connections["\\B"].extend(width, cell->parameters["\\B_SIGNED"].as_bool()); + cell->parameters["\\A_WIDTH"] = width; + cell->parameters["\\B_WIDTH"] = width; + } + + RTLIL::SigSpec a = cell->connections["\\A"]; + RTLIL::SigSpec b = cell->connections["\\B"]; + RTLIL::SigSpec new_a, new_b; + a.expand(), b.expand(); + + assert(a.chunks.size() == b.chunks.size()); + for (size_t i = 0; i < a.chunks.size(); i++) { + if (a.chunks[i].wire == NULL && a.chunks[i].data.bits[0] > RTLIL::State::S1) + continue; + if (b.chunks[i].wire == NULL && b.chunks[i].data.bits[0] > RTLIL::State::S1) + continue; + new_a.append(a.chunks[i]); + new_b.append(b.chunks[i]); + } + + if (new_a.width != a.width) { + new_a.optimize(); + new_b.optimize(); + cell->connections["\\A"] = new_a; + cell->connections["\\B"] = new_b; + cell->parameters["\\A_WIDTH"] = new_a.width; + cell->parameters["\\B_WIDTH"] = new_b.width; + } + + if (new_a.width == 0) { + replace_cell(module, cell, "empty", "\\Y", RTLIL::SigSpec(cell->type == "$eq" ? RTLIL::State::S1 : RTLIL::State::S0)); + goto next_cell; + } + } + +#define FOLD_1ARG_CELL(_t) \ + if (cell->type == "$" #_t) { \ + RTLIL::SigSpec a = cell->connections["\\A"]; \ + assign_map.apply(a); \ + if (a.is_fully_const()) { \ + a.optimize(); \ + RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \ + RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, dummy_arg, \ + cell->parameters["\\A_SIGNED"].as_bool(), false, \ + cell->parameters["\\Y_WIDTH"].as_int())); \ + replace_cell(module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ + goto next_cell; \ + } \ + } +#define FOLD_2ARG_CELL(_t) \ + if (cell->type == "$" #_t) { \ + RTLIL::SigSpec a = cell->connections["\\A"]; \ + RTLIL::SigSpec b = cell->connections["\\B"]; \ + assign_map.apply(a), assign_map.apply(b); \ + if (a.is_fully_const() && b.is_fully_const()) { \ + a.optimize(), b.optimize(); \ + RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, b.chunks[0].data, \ + cell->parameters["\\A_SIGNED"].as_bool(), \ + cell->parameters["\\B_SIGNED"].as_bool(), \ + cell->parameters["\\Y_WIDTH"].as_int())); \ + replace_cell(module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ + goto next_cell; \ + } \ + } + + FOLD_1ARG_CELL(not) + FOLD_2ARG_CELL(and) + FOLD_2ARG_CELL(or) + FOLD_2ARG_CELL(xor) + FOLD_2ARG_CELL(xnor) + + FOLD_1ARG_CELL(reduce_and) + FOLD_1ARG_CELL(reduce_or) + FOLD_1ARG_CELL(reduce_xor) + FOLD_1ARG_CELL(reduce_xnor) + FOLD_1ARG_CELL(reduce_bool) + + FOLD_1ARG_CELL(logic_not) + FOLD_2ARG_CELL(logic_and) + FOLD_2ARG_CELL(logic_or) + + FOLD_2ARG_CELL(shl) + FOLD_2ARG_CELL(shr) + FOLD_2ARG_CELL(sshl) + FOLD_2ARG_CELL(sshr) + + FOLD_2ARG_CELL(lt) + FOLD_2ARG_CELL(le) + FOLD_2ARG_CELL(eq) + FOLD_2ARG_CELL(ne) + FOLD_2ARG_CELL(gt) + FOLD_2ARG_CELL(ge) + + FOLD_2ARG_CELL(add) + FOLD_2ARG_CELL(sub) + FOLD_2ARG_CELL(mul) + FOLD_2ARG_CELL(div) + FOLD_2ARG_CELL(mod) + FOLD_2ARG_CELL(pow) + + FOLD_1ARG_CELL(pos) + FOLD_1ARG_CELL(neg) + + if (cell->type == "$mux") { + RTLIL::SigSpec input = cell->connections["\\S"]; + assign_map.apply(input); + if (input.is_fully_const()) + ACTION_DO("\\Y", input.as_bool() ? cell->connections["\\B"] : cell->connections["\\A"]); + } + + next_cell:; +#undef ACTION_DO +#undef ACTION_DO_Y +#undef FOLD_1ARG_CELL +#undef FOLD_2ARG_CELL + } +} + +struct OptConstPass : public Pass { + OptConstPass() : Pass("opt_const") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing OPT_CONST pass (perform const folding).\n"); + log_push(); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + do { + did_something = false; + replace_const_cells(mod_it.second); + } while (did_something); + + log_pop(); + } +} OptConstPass; + diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc new file mode 100644 index 000000000..d1f4e7b10 --- /dev/null +++ b/passes/opt/opt_muxtree.cc @@ -0,0 +1,417 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include "kernel/celltypes.h" +#include +#include +#include +#include + +struct OptMuxtreeWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap assign_map; + int removed_count; + + typedef std::pair bitDef_t; + + struct bitinfo_t { + int num; + bitDef_t bit; + bool seen_non_mux; + std::vector mux_users; + std::vector mux_drivers; + }; + + std::map bit2num; + std::vector bit2info; + + struct portinfo_t { + std::vector ctrl_sigs; + std::vector input_sigs; + std::vector input_muxes; + bool const_activated; + bool enabled; + }; + + struct muxinfo_t { + RTLIL::Cell *cell; + std::vector ports; + }; + + std::vector mux2info; + + OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : + design(design), module(module), assign_map(module), removed_count(0) + { + log("Running muxtree optimizier on module %s..\n", module->name.c_str()); + + log(" Creating internal representation of mux trees.\n"); + + // Populate bit2info[]: + // .seen_non_mux + // .mux_users + // .mux_drivers + // Populate mux2info[].ports[]: + // .ctrl_sigs + // .input_sigs + // .const_activated + for (auto &cell_it : module->cells) + { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux") + { + RTLIL::SigSpec sig_a = cell->connections["\\A"]; + RTLIL::SigSpec sig_b = cell->connections["\\B"]; + RTLIL::SigSpec sig_s = cell->connections["\\S"]; + RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + + muxinfo_t muxinfo; + muxinfo.cell = cell; + + for (int i = 0; i < sig_s.width; i++) { + RTLIL::SigSpec sig = sig_b.extract(i*sig_a.width, sig_a.width); + RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1)); + portinfo_t portinfo; + for (int idx : sig2bits(sig)) { + add_to_list(bit2info[idx].mux_users, mux2info.size()); + add_to_list(portinfo.input_sigs, idx); + } + for (int idx : sig2bits(ctrl_sig)) + add_to_list(portinfo.ctrl_sigs, idx); + portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool(); + portinfo.enabled = false; + muxinfo.ports.push_back(portinfo); + } + + portinfo_t portinfo; + for (int idx : sig2bits(sig_a)) { + add_to_list(bit2info[idx].mux_users, mux2info.size()); + add_to_list(portinfo.input_sigs, idx); + } + portinfo.const_activated = false; + portinfo.enabled = false; + muxinfo.ports.push_back(portinfo); + + for (int idx : sig2bits(sig_y)) + add_to_list(bit2info[idx].mux_drivers, mux2info.size()); + + for (int idx : sig2bits(sig_s)) + bit2info[idx].seen_non_mux = true; + + mux2info.push_back(muxinfo); + } + else + { + for (auto &it : cell->connections) { + for (int idx : sig2bits(it.second)) + bit2info[idx].seen_non_mux = true; + } + } + } + for (auto &it : module->wires) { + if (it.second->port_output) + for (int idx : sig2bits(RTLIL::SigSpec(it.second))) + bit2info[idx].seen_non_mux = true; + } + + if (mux2info.size() == 0) { + log(" No muxes found in this module.\n"); + return; + } + + // Populate mux2info[].ports[]: + // .input_muxes + for (size_t i = 0; i < bit2info.size(); i++) + for (int j : bit2info[i].mux_users) + for (auto &p : mux2info[j].ports) { + if (is_in_list(p.input_sigs, i)) + for (int k : bit2info[i].mux_drivers) + add_to_list(p.input_muxes, k); + } + + log(" Evaluating internal representation of mux trees.\n"); + + std::set root_muxes; + for (auto &bi : bit2info) { + if (!bi.seen_non_mux) + continue; + for (int mux_idx : bi.mux_drivers) + root_muxes.insert(mux_idx); + } + for (int mux_idx : root_muxes) + eval_root_mux(mux_idx); + + log(" Analyzing evaluation results.\n"); + + for (auto &mi : mux2info) + { + std::vector live_ports; + for (size_t port_idx = 0; port_idx < mi.ports.size(); port_idx++) { + portinfo_t &pi = mi.ports[port_idx]; + if (pi.enabled) { + live_ports.push_back(port_idx); + } else { + log(" dead port %zd/%zd on %s %s.\n", port_idx+1, mi.ports.size(), + mi.cell->type.c_str(), mi.cell->name.c_str()); + OPT_DID_SOMETHING = true; + removed_count++; + } + } + + if (live_ports.size() == mi.ports.size()) + continue; + + if (live_ports.size() == 0) { + module->cells.erase(mi.cell->name); + delete mi.cell; + continue; + } + + RTLIL::SigSpec sig_a = mi.cell->connections["\\A"]; + RTLIL::SigSpec sig_b = mi.cell->connections["\\B"]; + RTLIL::SigSpec sig_s = mi.cell->connections["\\S"]; + RTLIL::SigSpec sig_y = mi.cell->connections["\\Y"]; + + RTLIL::SigSpec sig_ports = sig_b; + sig_ports.append(sig_a); + + if (live_ports.size() == 1) + { + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.width, sig_a.width); + module->connections.push_back(RTLIL::SigSig(sig_y, sig_in)); + module->cells.erase(mi.cell->name); + delete mi.cell; + } + else + { + RTLIL::SigSpec new_sig_a, new_sig_b, new_sig_s; + + for (size_t i = 0; i < live_ports.size(); i++) { + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.width, sig_a.width); + if (i == live_ports.size()-1) { + new_sig_a = sig_in; + } else { + new_sig_b.append(sig_in); + new_sig_s.append(sig_s.extract(live_ports[i], 1)); + } + } + + mi.cell->connections["\\A"] = new_sig_a; + mi.cell->connections["\\B"] = new_sig_b; + mi.cell->connections["\\S"] = new_sig_s; + if (new_sig_s.width == 1) { + mi.cell->type = "$mux"; + mi.cell->attributes.erase("\\S_WIDTH"); + } else { + mi.cell->attributes["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width); + } + } + } + } + + bool list_is_subset(const std::vector &sub, const std::vector &super) + { + for (int v : sub) + if (!is_in_list(super, v)) + return false; + return true; + } + + bool is_in_list(const std::vector &list, int value) + { + for (int v : list) + if (v == value) + return true; + return false; + } + + void add_to_list(std::vector &list, int value) + { + if (!is_in_list(list, value)) + list.push_back(value); + } + + std::vector sig2bits(RTLIL::SigSpec sig) + { + std::vector results; + assign_map.apply(sig); + sig.expand(); + for (auto &c : sig.chunks) + if (c.wire != NULL) { + bitDef_t bit(c.wire, c.offset); + if (bit2num.count(bit) == 0) { + bitinfo_t info; + info.num = bit2info.size(); + info.bit = bit; + info.seen_non_mux = false; + bit2info.push_back(info); + bit2num[info.bit] = info.num; + } + results.push_back(bit2num[bit]); + } + return results; + } + + struct knowledge_t + { + // database of known inactive signals + // the 2nd integer is a reference counter used to manage the + // list. when it is non-zero the signal in known to be inactive + std::map known_inactive; + + // database of known active signals + // the 2nd dimension is the list of or-ed signals. so we know that + // for each i there is a j so that known_active[i][j] points to an + // inactive control signal. + std::vector> known_active; + + // this is just used to keep track of visited muxes in order to prohibit + // endless recursion in mux loops + std::set visited_muxes; + }; + + void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx) + { + muxinfo_t &muxinfo = mux2info[mux_idx]; + muxinfo.ports[port_idx].enabled = true; + + for (size_t i = 0; i < muxinfo.ports.size(); i++) { + if (int(i) == port_idx) + continue; + for (int b : muxinfo.ports[i].ctrl_sigs) + knowledge.known_inactive[b]++; + } + + if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) + knowledge.known_active.push_back(muxinfo.ports[port_idx].ctrl_sigs); + + for (int m : muxinfo.ports[port_idx].input_muxes) { + if (knowledge.visited_muxes.count(m) > 0) + continue; + knowledge.visited_muxes.insert(m); + eval_mux(knowledge, m); + knowledge.visited_muxes.erase(m); + } + + if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) + knowledge.known_active.pop_back(); + + for (size_t i = 0; i < muxinfo.ports.size(); i++) { + if (int(i) == port_idx) + continue; + for (int b : muxinfo.ports[i].ctrl_sigs) + knowledge.known_inactive[b]--; + } + } + + void eval_mux(knowledge_t &knowledge, int mux_idx) + { + muxinfo_t &muxinfo = mux2info[mux_idx]; + + // if there is a constant activated port we just use it + for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++) + { + portinfo_t &portinfo = muxinfo.ports[port_idx]; + if (portinfo.const_activated) { + eval_mux_port(knowledge, mux_idx, port_idx); + return; + } + } + + // compare ports with known_active signals. if we find a match, only this + // port can be active. do not include the last port (its the default port + // that has no control signals). + for (size_t port_idx = 0; port_idx < muxinfo.ports.size()-1; port_idx++) + { + portinfo_t &portinfo = muxinfo.ports[port_idx]; + for (size_t i = 0; i < knowledge.known_active.size(); i++) { + if (list_is_subset(knowledge.known_active[i], portinfo.ctrl_sigs)) { + eval_mux_port(knowledge, mux_idx, port_idx); + return; + } + } + } + + // compare ports with known_inactive and known_active signals. If all control + // signals of the port are know_inactive or if the control signals of all other + // ports are known_active this port can't be activated. this loop includes the + // default port but no known_inactive match is performed on the default port. + for (size_t port_idx = 0; port_idx < muxinfo.ports.size(); port_idx++) + { + portinfo_t &portinfo = muxinfo.ports[port_idx]; + + if (port_idx < muxinfo.ports.size()-1) { + bool found_non_known_inactive = false; + for (int i : portinfo.ctrl_sigs) + if (knowledge.known_inactive[i] == 0) + found_non_known_inactive = true; + if (!found_non_known_inactive) + continue; + } + + bool port_active = true; + std::vector other_ctrl_sig; + for (size_t i = 0; i < muxinfo.ports.size()-1; i++) { + if (i == port_idx) + continue; + other_ctrl_sig.insert(other_ctrl_sig.end(), + muxinfo.ports[i].ctrl_sigs.begin(), muxinfo.ports[i].ctrl_sigs.end()); + } + for (size_t i = 0; i < knowledge.known_active.size(); i++) { + if (list_is_subset(knowledge.known_active[i], other_ctrl_sig)) + port_active = false; + } + if (port_active) + eval_mux_port(knowledge, mux_idx, port_idx); + } + } + + void eval_root_mux(int mux_idx) + { + knowledge_t knowledge; + eval_mux(knowledge, mux_idx); + } +}; + +struct OptMuxtreePass : public Pass { + OptMuxtreePass() : Pass("opt_muxtree") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); + extra_args(args, 1, design); + + int total_count = 0; + for (auto &mod_it : design->modules) { + if (mod_it.second->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str()); + } else { + OptMuxtreeWorker worker(design, mod_it.second); + total_count += worker.removed_count; + } + } + log("Removed %d multiplexer ports.\n", total_count); + } +} OptMuxtreePass; + diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc new file mode 100644 index 000000000..9a1b263a0 --- /dev/null +++ b/passes/opt/opt_reduce.cc @@ -0,0 +1,236 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include "kernel/sha1.h" +#include "kernel/celltypes.h" +#include +#include +#include +#include + +struct OptReduceWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap assign_map; + + int total_count; + bool did_something; + + void opt_reduce(std::set &cells, SigSet &drivers, RTLIL::Cell *cell) + { + if (cells.count(cell) == 0) + return; + cells.erase(cell); + + RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); + sig_a.sort_and_unify(); + sig_a.expand(); + + RTLIL::SigSpec new_sig_a; + for (auto &chunk : sig_a.chunks) + { + if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S0) { + if (cell->type == "$reduce_and") { + new_sig_a = RTLIL::SigSpec(RTLIL::State::S0); + break; + } + continue; + } + if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S1) { + if (cell->type == "$reduce_or") { + new_sig_a = RTLIL::SigSpec(RTLIL::State::S1); + break; + } + continue; + } + if (chunk.wire == NULL) { + new_sig_a = RTLIL::SigSpec(RTLIL::State::Sx); + break; + } + + bool imported_children = false; + for (auto child_cell : drivers.find(chunk)) { + if (child_cell->type == cell->type) { + opt_reduce(cells, drivers, child_cell); + new_sig_a.append(child_cell->connections["\\A"]); + imported_children = true; + } + } + if (!imported_children) + new_sig_a.append(chunk); + } + new_sig_a.sort_and_unify(); + + if (new_sig_a != sig_a || sig_a.width != cell->connections["\\A"].width) { + log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a)); + did_something = true; + OPT_DID_SOMETHING = true; + total_count++; + } + + cell->connections["\\A"] = new_sig_a; + cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.width); + return; + } + + void opt_mux(RTLIL::Cell *cell) + { + RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); + RTLIL::SigSpec sig_b = assign_map(cell->connections["\\B"]); + RTLIL::SigSpec sig_s = assign_map(cell->connections["\\S"]); + + RTLIL::SigSpec new_sig_b, new_sig_s; + std::set handled_sig; + + handled_sig.insert(sig_a); + for (int i = 0; i < sig_s.width; i++) + { + RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.width, sig_a.width); + if (handled_sig.count(this_b) > 0) + continue; + + RTLIL::SigSpec this_s = sig_s.extract(i, 1); + for (int j = i+1; j < sig_s.width; j++) { + RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.width, sig_a.width); + if (this_b == that_b) + this_s.append(sig_s.extract(j, 1)); + } + + if (this_s.width > 1) + { + RTLIL::Wire *reduce_or_wire = new RTLIL::Wire; + reduce_or_wire->name = NEW_ID; + module->wires[reduce_or_wire->name] = reduce_or_wire; + + RTLIL::Cell *reduce_or_cell = new RTLIL::Cell; + reduce_or_cell->name = NEW_ID; + reduce_or_cell->type = "$reduce_or"; + reduce_or_cell->connections["\\A"] = this_s; + reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.width); + reduce_or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + module->cells[reduce_or_cell->name] = reduce_or_cell; + + this_s = RTLIL::SigSpec(reduce_or_wire); + reduce_or_cell->connections["\\Y"] = this_s; + } + + new_sig_b.append(this_b); + new_sig_s.append(this_s); + handled_sig.insert(this_b); + } + + if (new_sig_s.width != sig_s.width) { + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + OPT_DID_SOMETHING = true; + total_count++; + } + + if (new_sig_s.width == 0) + { + module->connections.push_back(RTLIL::SigSig(cell->connections["\\Y"], cell->connections["\\A"])); + assign_map.add(cell->connections["\\Y"], cell->connections["\\A"]); + module->cells.erase(cell->name); + delete cell; + } + else + { + cell->connections["\\B"] = new_sig_b; + cell->connections["\\S"] = new_sig_s; + if (new_sig_s.width > 1) { + cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width); + } else { + cell->type = "$mux"; + cell->parameters.erase("\\S_WIDTH"); + } + } + } + + OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module) : + design(design), module(module), assign_map(module) + { + log(" Optimizing cells in module %s.\n", module->name.c_str()); + + total_count = 0; + did_something = true; + + while (did_something) + { + did_something = false; + + // merge trees of reduce_* cells to one single cell and unify input vectors + // (only handle recduce_and and reduce_or for various reasons) + + const char *type_list[] = { "$reduce_or", "$reduce_and" }; + for (auto type : type_list) + { + SigSet drivers; + std::set cells; + + for (auto &cell_it : module->cells) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type != type || !design->selected(module, cell)) + continue; + drivers.insert(assign_map(cell->connections["\\Y"]), cell); + cells.insert(cell); + } + + while (cells.size() > 0) { + RTLIL::Cell *cell = *cells.begin(); + opt_reduce(cells, drivers, cell); + } + } + + // merge identical inputs on $mux and $pmux cells + + for (auto &cell_it : module->cells) + { + RTLIL::Cell *cell = cell_it.second; + if ((cell->type != "$mux" && cell->type != "$pmux" && cell->type != "$safe_pmux") || !design->selected(module, cell)) + continue; + opt_mux(cell); + } + } + } +}; + +struct OptReducePass : public Pass { + OptReducePass() : Pass("opt_reduce") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); + extra_args(args, 1, design); + + int total_count = 0; + for (auto &mod_it : design->modules) { + if (!design->selected(mod_it.second)) + continue; + OptReduceWorker worker(design, mod_it.second); + total_count += worker.total_count; + } + + log("Performed a total of %d changes.\n", total_count); + } +} OptReducePass; + diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc new file mode 100644 index 000000000..384a4d85f --- /dev/null +++ b/passes/opt/opt_rmdff.cc @@ -0,0 +1,135 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include +#include + +static SigMap assign_map; + +static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) +{ + RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r; + RTLIL::Const val_cp, val_rp, val_rv; + + if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") { + sig_d = dff->connections["\\D"]; + sig_q = dff->connections["\\Q"]; + sig_c = dff->connections["\\C"]; + val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1); + } + else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" && + (dff->type[6] == 'N' || dff->type[6] == 'P') && + (dff->type[7] == 'N' || dff->type[7] == 'P') && + (dff->type[8] == '0' || dff->type[8] == '1')) { + sig_d = dff->connections["\\D"]; + sig_q = dff->connections["\\Q"]; + sig_c = dff->connections["\\C"]; + sig_r = dff->connections["\\R"]; + val_cp = RTLIL::Const(dff->type[6] == 'P', 1); + val_rp = RTLIL::Const(dff->type[7] == 'P', 1); + val_rv = RTLIL::Const(dff->type[8] == '1', 1); + } + else if (dff->type == "$dff") { + sig_d = dff->connections["\\D"]; + sig_q = dff->connections["\\Q"]; + sig_c = dff->connections["\\CLK"]; + val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); + } + else if (dff->type == "$adff") { + sig_d = dff->connections["\\D"]; + sig_q = dff->connections["\\Q"]; + sig_c = dff->connections["\\CLK"]; + sig_r = dff->connections["\\ARST"]; + val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); + val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1); + val_rv = dff->parameters["\\ARST_VALUE"]; + } + else + log_error("abort."); + + assign_map.apply(sig_d); + assign_map.apply(sig_q); + assign_map.apply(sig_c); + assign_map.apply(sig_r); + + if (sig_d.is_fully_const() && sig_r.width == 0) { + RTLIL::SigSig conn(sig_q, sig_d); + mod->connections.push_back(conn); + goto delete_dff; + } + + if (sig_d == sig_q && sig_r.width == 0) { + goto delete_dff; + } + + return false; + +delete_dff: + log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); + OPT_DID_SOMETHING = true; + mod->cells.erase(dff->name); + delete dff; + return true; +} + +struct OptRmdffPass : public Pass { + OptRmdffPass() : Pass("opt_rmdff") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + int total_count = 0; + log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n"); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + { + assign_map.set(mod_it.second); + + std::vector dff_list; + for (auto &it : mod_it.second->cells) { + if (it.second->type == "$_DFF_N_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_P_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_NN0_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_NN1_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_NP0_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_NP1_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_PN0_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_PN1_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_PP0_") dff_list.push_back(it.first); + if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first); + if (it.second->type == "$dff") dff_list.push_back(it.first); + if (it.second->type == "$adff") dff_list.push_back(it.first); + } + + for (auto &id : dff_list) { + if (mod_it.second->cells.count(id) > 0 && + handle_dff(mod_it.second, mod_it.second->cells[id])) + total_count++; + } + } + + assign_map.clear(); + log("Replaced %d DFF cells.\n", total_count); + } +} OptRmdffPass; + diff --git a/passes/opt/opt_rmunused.cc b/passes/opt/opt_rmunused.cc new file mode 100644 index 000000000..29a6f2bc4 --- /dev/null +++ b/passes/opt/opt_rmunused.cc @@ -0,0 +1,239 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include "kernel/celltypes.h" +#include +#include +#include +#include + +static CellTypes ct; + +static void rmunused_module_cells(RTLIL::Module *module) +{ + SigMap assign_map(module); + std::set queue, unused; + + SigSet wire2driver; + for (auto &it : module->cells) { + RTLIL::Cell *cell = it.second; + for (auto &it2 : cell->connections) { + if (!ct.cell_input(cell->type, it2.first)) { + RTLIL::SigSpec sig = it2.second; + assign_map.apply(sig); + wire2driver.insert(sig, cell); + } + } + if (cell->type == "$memwr") + queue.insert(cell); + unused.insert(cell); + } + + for (auto &it : module->wires) { + RTLIL::Wire *wire = it.second; + if (wire->port_output) { + std::set cell_list; + RTLIL::SigSpec sig = RTLIL::SigSpec(wire); + assign_map.apply(sig); + wire2driver.find(sig, cell_list); + for (auto cell : cell_list) + queue.insert(cell); + } + } + + while (queue.size() > 0) + { + std::set new_queue; + for (auto cell : queue) + unused.erase(cell); + for (auto cell : queue) { + for (auto &it : cell->connections) { + if (!ct.cell_output(cell->type, it.first)) { + std::set cell_list; + RTLIL::SigSpec sig = it.second; + assign_map.apply(sig); + wire2driver.find(sig, cell_list); + for (auto cell : cell_list) { + if (unused.count(cell) > 0) + new_queue.insert(cell); + } + } + } + } + queue.swap(new_queue); + } + + for (auto cell : unused) { + log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str()); + OPT_DID_SOMETHING = true; + module->cells.erase(cell->name); + delete cell; + } +} + +static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2) +{ + assert(s1.width == 1); + assert(s2.width == 1); + assert(s1.chunks.size() == 1); + assert(s2.chunks.size() == 1); + + RTLIL::Wire *w1 = s1.chunks[0].wire; + RTLIL::Wire *w2 = s2.chunks[0].wire; + + if (w1 == NULL || w2 == NULL) + return w2 == NULL; + + if (w1->port_input != w2->port_input) + return w2->port_input; + + if (w1->name[0] != w2->name[0]) + return w2->name[0] == '\\'; + + if (w1->attributes.size() != w2->attributes.size()) + return w2->attributes.size() > w1->attributes.size(); + + return w2->name < w1->name; +} + +static void rmunused_module_signals(RTLIL::Module *module) +{ + SigMap assign_map(module); + for (auto &it : module->wires) { + RTLIL::Wire *wire = it.second; + for (int i = 0; i < wire->width; i++) { + RTLIL::SigSpec s1 = RTLIL::SigSpec(wire, 1, i), s2 = assign_map(s1); + if (!compare_signals(s1, s2)) + assign_map.add(s1); + } + } + + module->connections.clear(); + + SigPool used_signals; + SigPool used_signals_nodrivers; + for (auto &it : module->cells) { + RTLIL::Cell *cell = it.second; + for (auto &it2 : cell->connections) { + assign_map.apply(it2.second); + used_signals.add(it2.second); + if (!ct.cell_output(cell->type, it2.first)) + used_signals_nodrivers.add(it2.second); + } + } + + std::vector del_wires; + for (auto &it : module->wires) { + RTLIL::Wire *wire = it.second; + if (wire->name[0] == '\\') { + RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; + assign_map.apply(s2); + if (!used_signals.check_any(s2) && wire->port_id == 0) { + log(" removing unused non-port wire %s.\n", wire->name.c_str()); + del_wires.push_back(wire); + } else { + s1.expand(); + s2.expand(); + assert(s1.chunks.size() == s2.chunks.size()); + RTLIL::SigSig new_conn; + for (size_t i = 0; i < s1.chunks.size(); i++) + if (s1.chunks[i] != s2.chunks[i]) { + new_conn.first.append(s1.chunks[i]); + new_conn.second.append(s2.chunks[i]); + } + if (new_conn.first.width > 0) { + new_conn.first.optimize(); + new_conn.second.optimize(); + module->connections.push_back(new_conn); + } + } + } else { + if (!used_signals.check_any(RTLIL::SigSpec(wire))) + del_wires.push_back(wire); + } + RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); + if (!used_signals_nodrivers.check_any(sig)) { + std::string unused_bits; + sig.expand(); + for (size_t i = 0; i < sig.chunks.size(); i++) { + if (sig.chunks[i].wire == NULL) + continue; + if (!used_signals_nodrivers.check_any(sig)) { + if (!unused_bits.empty()) + unused_bits += " "; + unused_bits += stringf("%zd", i); + } + } + if (unused_bits.empty() || wire->port_id != 0) + wire->attributes.erase("\\unused_bits"); + else + wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits); + } else { + wire->attributes.erase("\\unused_bits"); + } + } + + for (auto wire : del_wires) { + module->wires.erase(wire->name); + delete wire; + } + + if (del_wires.size() > 0) + log(" removed %zd unused temporary wires.\n", del_wires.size()); +} + +static void rmunused_module(RTLIL::Module *module) +{ + log("Finding unused cells or wires in module %s..\n", module->name.c_str()); + + rmunused_module_cells(module); + rmunused_module_signals(module); +} + +struct OptRmUnusedPass : public Pass { + OptRmUnusedPass() : Pass("opt_rmunused") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing OPT_RMUNUSED pass (remove unused cells and wires).\n"); + log_push(); + + extra_args(args, 1, design); + + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + for (auto &mod_it : design->modules) { + if (mod_it.second->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", mod_it.second->name.c_str()); + } else { + rmunused_module(mod_it.second); + } + } + + ct.clear(); + log_pop(); + } +} OptRmUnusedPass; + diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc new file mode 100644 index 000000000..ecad8b436 --- /dev/null +++ b/passes/opt/opt_share.cc @@ -0,0 +1,250 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "opt_status.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include "kernel/sha1.h" +#include "kernel/celltypes.h" +#include +#include +#include +#include + +#define USE_CELL_HASH_CACHE + +struct OptShareWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap assign_map; + + CellTypes ct; + int total_count; +#ifdef USE_CELL_HASH_CACHE + std::map cell_hash_cache; +#endif + +#ifdef USE_CELL_HASH_CACHE + std::string int_to_hash_string(unsigned int v) + { + if (v == 0) + return "0"; + std::string str = ""; + while (v > 0) { + str += 'a' + (v & 15); + v = v >> 4; + } + return str; + } + + std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell) + { + if (cell_hash_cache.count(cell) > 0) + return cell_hash_cache[cell]; + + std::string hash_string = cell->type + "\n"; + + for (auto &it : cell->parameters) + hash_string += "P " + it.first + "=" + it.second.as_string() + "\n"; + + for (auto &it : cell->connections) { + if (ct.cell_output(cell->type, it.first)) + continue; + RTLIL::SigSpec sig = it.second; + assign_map.apply(sig); + hash_string += "C " + it.first + "="; + for (auto &chunk : sig.chunks) { + if (chunk.wire) + hash_string += "{" + chunk.wire->name + " " + + int_to_hash_string(chunk.offset) + " " + + int_to_hash_string(chunk.width) + "}"; + else + hash_string += chunk.data.as_string(); + } + hash_string += "\n"; + } + + unsigned char hash[20]; + char hash_hex_string[41]; + sha1::calc(hash_string.c_str(), hash_string.size(), hash); + sha1::toHexString(hash, hash_hex_string); + cell_hash_cache[cell] = hash_hex_string; + + return cell_hash_cache[cell]; + } +#endif + + bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2, bool <) + { +#ifdef USE_CELL_HASH_CACHE + std::string hash1 = hash_cell_parameters_and_connections(cell1); + std::string hash2 = hash_cell_parameters_and_connections(cell2); + + if (hash1 != hash2) { + lt = hash1 < hash2; + return true; + } +#endif + + if (cell1->parameters != cell2->parameters) { + lt = cell1->parameters < cell2->parameters; + return true; + } + + std::map conn1 = cell1->connections; + std::map conn2 = cell2->connections; + + for (auto &it : conn1) { + if (ct.cell_output(cell1->type, it.first)) + it.second = RTLIL::SigSpec(); + else + assign_map.apply(it.second); + } + + for (auto &it : conn2) { + if (ct.cell_output(cell2->type, it.first)) + it.second = RTLIL::SigSpec(); + else + assign_map.apply(it.second); + } + + if (conn1 != conn2) { + lt = conn1 < conn2; + return true; + } + + return false; + } + + bool compare_cells(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) + { + if (cell1->type != cell2->type) + return cell1->type < cell2->type; + + if (!ct.cell_known(cell1->type)) + return cell1 < cell2; + + bool lt; + if (compare_cell_parameters_and_connections(cell1, cell2, lt)) + return lt; + + return false; + } + + struct CompareCells { + OptShareWorker *that; + CompareCells(OptShareWorker *that) : that(that) {} + bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { + return that->compare_cells(cell1, cell2); + } + }; + + OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux) : + design(design), module(module), assign_map(module) + { + total_count = 0; + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + if (mode_nomux) { + ct.cell_types.erase("$mux"); + ct.cell_types.erase("$pmux"); + ct.cell_types.erase("$safe_pmux"); + } + + log("Finding identical cells in module `%s'.\n", module->name.c_str()); + assign_map.set(module); + + bool did_something = true; + while (did_something) + { +#ifdef USE_CELL_HASH_CACHE + cell_hash_cache.clear(); +#endif + std::vector cells; + cells.reserve(module->cells.size()); + for (auto &it : module->cells) { + if (ct.cell_known(it.second->type) && design->selected(module, it.second)) + cells.push_back(it.second); + } + + did_something = false; + std::map sharemap(CompareCells(this)); + for (auto cell : cells) + { + if (sharemap.count(cell) > 0) { + did_something = true; + log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); + for (auto &it : cell->connections) { + if (ct.cell_output(cell->type, it.first)) { + RTLIL::SigSpec other_sig = sharemap[cell]->connections[it.first]; + log(" Redirecting output %s: %s = %s\n", it.first.c_str(), + log_signal(it.second), log_signal(other_sig)); + module->connections.push_back(RTLIL::SigSig(it.second, other_sig)); + assign_map.add(it.second, other_sig); + } + } + log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + module->cells.erase(cell->name); + OPT_DID_SOMETHING = true; + total_count++; + delete cell; + } else { + sharemap[cell] = cell; + } + } + } + } +}; + +struct OptSharePass : public Pass { + OptSharePass() : Pass("opt_share") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing OPT_SHARE pass (detect identical cells).\n"); + + bool mode_nomux = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-nomux") { + mode_nomux = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + int total_count = 0; + for (auto &mod_it : design->modules) { + if (!design->selected(mod_it.second)) + continue; + OptShareWorker worker(design, mod_it.second, mode_nomux); + total_count += worker.total_count; + } + + log("Removed a total of %d cells.\n", total_count); + } +} OptSharePass; + diff --git a/passes/opt/opt_status.h b/passes/opt/opt_status.h new file mode 100644 index 000000000..3d12baa7d --- /dev/null +++ b/passes/opt/opt_status.h @@ -0,0 +1,26 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef OPT_STATUS_H +#define OPT_STATUS_H + +extern bool OPT_DID_SOMETHING; + +#endif + diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc new file mode 100644 index 000000000..68564e055 --- /dev/null +++ b/passes/proc/Makefile.inc @@ -0,0 +1,8 @@ + +OBJS += passes/proc/proc.o +OBJS += passes/proc/proc_clean.o +OBJS += passes/proc/proc_rmdead.o +OBJS += passes/proc/proc_arst.o +OBJS += passes/proc/proc_mux.o +OBJS += passes/proc/proc_dff.o + diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc new file mode 100644 index 000000000..c0f502c83 --- /dev/null +++ b/passes/proc/proc.cc @@ -0,0 +1,44 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +struct ProcPass : public Pass { + ProcPass() : Pass("proc") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing PROC pass (convert processes to netlists).\n"); + log_push(); + + extra_args(args, 1, design); + + Pass::call(design, "proc_clean"); + Pass::call(design, "proc_rmdead"); + Pass::call(design, "proc_arst"); + Pass::call(design, "proc_mux"); + Pass::call(design, "proc_dff"); + Pass::call(design, "proc_clean"); + + log_pop(); + } +} ProcPass; + diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc new file mode 100644 index 000000000..8e57d0efc --- /dev/null +++ b/passes/proc/proc_arst.cc @@ -0,0 +1,191 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include +#include + +static bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref, bool &polarity) +{ + if (signal.width != 1) + return false; + if (signal == ref) + return true; + + for (auto &cell_it : mod->cells) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$reduce_or" && cell->connections["\\Y"] == signal) + return check_signal(mod, cell->connections["\\A"], ref, polarity); + if (cell->type == "$reduce_bool" && cell->connections["\\Y"] == signal) + return check_signal(mod, cell->connections["\\A"], ref, polarity); + if (cell->type == "$logic_not" && cell->connections["\\Y"] == signal) { + polarity = !polarity; + return check_signal(mod, cell->connections["\\A"], ref, polarity); + } + if (cell->type == "$not" && cell->connections["\\Y"] == signal) { + polarity = !polarity; + return check_signal(mod, cell->connections["\\A"], ref, polarity); + } + if (cell->type == "$eq" && cell->connections["\\Y"] == signal) { + if (cell->connections["\\A"].is_fully_const()) { + if (!cell->connections["\\A"].as_bool()) + polarity = !polarity; + return check_signal(mod, cell->connections["\\B"], ref, polarity); + } + if (cell->connections["\\B"].is_fully_const()) { + if (!cell->connections["\\B"].as_bool()) + polarity = !polarity; + return check_signal(mod, cell->connections["\\A"], ref, polarity); + } + } + if (cell->type == "$ne" && cell->connections["\\Y"] == signal) { + if (cell->connections["\\A"].is_fully_const()) { + if (cell->connections["\\A"].as_bool()) + polarity = !polarity; + return check_signal(mod, cell->connections["\\B"], ref, polarity); + } + if (cell->connections["\\B"].is_fully_const()) { + if (cell->connections["\\B"].as_bool()) + polarity = !polarity; + return check_signal(mod, cell->connections["\\A"], ref, polarity); + } + } + } + + return false; +} + +static void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec &rval, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity, bool unknown) +{ + for (auto &action : cs->actions) { + if (unknown) + rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.width), &rval); + else + rspec.replace(action.first, action.second, &rval); + } + + for (auto sw : cs->switches) { + if (sw->signal.width == 0) { + for (auto cs2 : sw->cases) + apply_const(mod, rspec, rval, cs2, const_sig, polarity, unknown); + } + bool this_polarity = polarity; + if (check_signal(mod, sw->signal, const_sig, this_polarity)) { + for (auto cs2 : sw->cases) { + for (auto comp : cs2->compare) + if (comp == RTLIL::SigSpec(this_polarity, 1)) + goto matched_case; + if (cs2->compare.size() == 0) { + matched_case: + apply_const(mod, rspec, rval, cs2, const_sig, polarity, false); + break; + } + } + } else { + for (auto cs2 : sw->cases) + apply_const(mod, rspec, rval, cs2, const_sig, polarity, true); + } + } +} + +static void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec const_sig, bool polarity) +{ + for (auto sw : cs->switches) { + bool this_polarity = polarity; + if (check_signal(mod, sw->signal, const_sig, this_polarity)) { + bool found_rem_path = false; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + for (auto comp : cs2->compare) + if (comp == RTLIL::SigSpec(this_polarity, 1)) + goto matched_case; + if (found_rem_path) { + matched_case: + sw->cases.erase(sw->cases.begin() + (i--)); + delete cs2; + continue; + } + found_rem_path = true; + cs2->compare.clear(); + } + sw->signal = RTLIL::SigSpec(); + } else { + for (auto cs2 : sw->cases) + eliminate_const(mod, cs2, const_sig, polarity); + } + } +} + +static void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) +{ + if (proc->root_case.switches.size() != 1) + return; + + RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal; + + for (auto &sync : proc->syncs) { + if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { + bool polarity = sync->type == RTLIL::SyncType::STp; + if (check_signal(mod, root_sig, sync->signal, polarity)) { + log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); + sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; + for (auto &action : sync->actions) { + RTLIL::SigSpec rspec = action.second; + RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.width); + RTLIL::SigSpec last_rval; + for (int count = 0; rval != last_rval; count++) { + last_rval = rval; + apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false); + assign_map.apply(rval); + if (rval.is_fully_const()) + break; + if (count > 100) + log_error("Async reset %s yields endless loop at value %s for signal %s.\n", + log_signal(sync->signal), log_signal(rval), log_signal(action.first)); + rspec = rval; + } + if (rval.has_marked_bits()) + log_error("Async reset %s yields non-constant value %s for signal %s.\n", + log_signal(sync->signal), log_signal(rval), log_signal(action.first)); + action.second = rval; + } + eliminate_const(mod, &proc->root_case, root_sig, polarity); + } + } + } +} + +struct ProcArstPass : public Pass { + ProcArstPass() : Pass("proc_arst") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing PROC_ARST pass (detect async resets in processes).\n"); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) { + SigMap assign_map(mod_it.second); + for (auto &proc_it : mod_it.second->processes) + proc_arst(mod_it.second, proc_it.second, assign_map); + } + } +} ProcArstPass; + diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc new file mode 100644 index 000000000..ec9fade30 --- /dev/null +++ b/passes/proc/proc_clean.cc @@ -0,0 +1,160 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include + +static void switch_clean(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count); +static void case_clean(RTLIL::CaseRule *cs, bool &did_something, int &count); + +static void switch_clean(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count) +{ + if (sw->signal.width > 0 && sw->signal.is_fully_const()) + { + int found_matching_case_idx = -1; + for (int i = 0; i < int(sw->cases.size()) && found_matching_case_idx < 0; i++) + { + RTLIL::CaseRule *cs = sw->cases[i]; + if (cs->compare.size() == 0) + break; + for (int j = 0; j < int(cs->compare.size()); j++) { + RTLIL::SigSpec &val = cs->compare[j]; + if (!val.is_fully_const()) + continue; + if (val == sw->signal) { + cs->compare.clear(); + found_matching_case_idx = i; + break; + } else + cs->compare.erase(cs->compare.begin()+(j--)); + } + if (cs->compare.size() == 0 && found_matching_case_idx < 0) { + sw->cases.erase(sw->cases.begin()+(i--)); + delete cs; + } + } + while (found_matching_case_idx >= 0 && int(sw->cases.size()) > found_matching_case_idx+1) { + delete sw->cases.back(); + sw->cases.pop_back(); + } + if (found_matching_case_idx == 0) + sw->signal = RTLIL::SigSpec(); + } + + if (sw->cases.size() == 1 && (sw->signal.width == 0 || sw->cases[0]->compare.empty())) + { + did_something = true; + for (auto &action : sw->cases[0]->actions) + parent->actions.push_back(action); + for (auto sw2 : sw->cases[0]->switches) + parent->switches.push_back(sw2); + sw->cases[0]->switches.clear(); + delete sw->cases[0]; + sw->cases.clear(); + } + else + { + bool all_cases_are_empty = true; + for (auto cs : sw->cases) { + if (cs->actions.size() != 0 || cs->switches.size() != 0) + all_cases_are_empty = false; + case_clean(cs, did_something, count); + } + if (all_cases_are_empty) { + did_something = true; + for (auto cs : sw->cases) + delete cs; + sw->cases.clear(); + } + } +} + +static void case_clean(RTLIL::CaseRule *cs, bool &did_something, int &count) +{ + for (size_t i = 0; i < cs->actions.size(); i++) { + if (cs->actions[i].first.width == 0) { + did_something = true; + cs->actions.erase(cs->actions.begin() + (i--)); + } + } + for (size_t i = 0; i < cs->switches.size(); i++) { + RTLIL::SwitchRule *sw = cs->switches[i]; + if (sw->cases.size() == 0) { + cs->switches.erase(cs->switches.begin() + (i--)); + did_something = true; + delete sw; + count++; + } else + switch_clean(sw, cs, did_something, count); + } +} + +static void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) +{ + int count = 0; + bool did_something = true; + for (size_t i = 0; i < proc->syncs.size(); i++) { + for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++) + if (proc->syncs[i]->actions[j].first.width == 0) + proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--)); + if (proc->syncs[i]->actions.size() == 0) { + delete proc->syncs[i]; + proc->syncs.erase(proc->syncs.begin() + (i--)); + } + } + while (did_something) { + did_something = false; + case_clean(&proc->root_case, did_something, count); + } + if (count > 0) + log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str()); + total_count += count; +} + +struct ProcCleanPass : public Pass { + ProcCleanPass() : Pass("proc_clean") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + int total_count = 0; + log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) { + std::vector delme; + for (auto &proc_it : mod_it.second->processes) { + proc_clean(mod_it.second, proc_it.second, total_count); + if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 && + proc_it.second->root_case.actions.size() == 0) { + log("Removing empty process `%s.%s'.\n", mod_it.first.c_str(), proc_it.second->name.c_str()); + delme.push_back(proc_it.first); + } + } + for (auto &id : delme) { + delete mod_it.second->processes[id]; + mod_it.second->processes.erase(id); + } + } + + log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); + } +} ProcCleanPass; + diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc new file mode 100644 index 000000000..ccacc72d2 --- /dev/null +++ b/passes/proc/proc_dff.cc @@ -0,0 +1,178 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/log.h" +#include +#include +#include +#include + +static RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) +{ + RTLIL::SigSpec lvalue; + + for (auto sync : proc->syncs) + for (auto &action : sync->actions) + if (action.first.width > 0) { + lvalue = action.first; + lvalue.sort_and_unify(); + break; + } + + for (auto sync : proc->syncs) { + RTLIL::SigSpec this_lvalue; + for (auto &action : sync->actions) + this_lvalue.append(action.first); + this_lvalue.sort_and_unify(); + RTLIL::SigSpec common_sig = this_lvalue.extract(lvalue); + if (common_sig.width > 0) + lvalue = common_sig; + } + + return lvalue; +} + +static void gen_dff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::Const val_rst, RTLIL::SigSpec sig_out, + bool clk_polarity, bool arst_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec *arst, RTLIL::Process *proc) +{ + std::stringstream sstr; + sstr << "$procdff$" << (RTLIL::autoidx++); + + RTLIL::Cell *cell = new RTLIL::Cell; + cell->name = sstr.str(); + cell->type = arst ? "$adff" : "$dff"; + cell->attributes = proc->attributes; + mod->cells[cell->name] = cell; + + cell->parameters["\\WIDTH"] = RTLIL::Const(sig_in.width); + if (arst) { + cell->parameters["\\ARST_POLARITY"] = RTLIL::Const(arst_polarity, 1); + cell->parameters["\\ARST_VALUE"] = val_rst; + } + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity, 1); + + cell->connections["\\D"] = sig_in; + cell->connections["\\Q"] = sig_out; + if (arst) + cell->connections["\\ARST"] = *arst; + cell->connections["\\CLK"] = clk; + + log(" created %s cell `%s' with %s edge clock", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); + if (arst) + log(" and %s level reset", arst_polarity ? "positive" : "negative"); + log(".\n"); +} + +static void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) +{ + while (1) + { + RTLIL::SigSpec sig = find_any_lvalue(proc); + + if (sig.width == 0) + break; + + log("Creating register for signal `%s.%s' using process `%s.%s'.\n", + mod->name.c_str(), log_signal(sig), mod->name.c_str(), proc->name.c_str()); + + RTLIL::SigSpec insig = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); + RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.width); + RTLIL::SyncRule *sync_level = NULL; + RTLIL::SyncRule *sync_edge = NULL; + RTLIL::SyncRule *sync_always = NULL; + + for (auto sync : proc->syncs) + for (auto &action : sync->actions) + { + if (action.first.extract(sig).width == 0) + continue; + + if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) { + if (sync_level != NULL && sync_level != sync) + log_error("Multiple level sensitive events found for this signal!\n"); + sig.replace(action.first, action.second, &rstval); + sync_level = sync; + } + else if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { + if (sync_edge != NULL && sync_edge != sync) + log_error("Multiple edge sensitive events found for this signal!\n"); + sig.replace(action.first, action.second, &insig); + sync_edge = sync; + } + else if (sync->type == RTLIL::SyncType::STa) { + if (sync_always != NULL && sync_always != sync) + log_error("Multiple always events found for this signal!\n"); + sig.replace(action.first, action.second, &insig); + sync_always = sync; + } + else { + log_error("Event with any-edge sensitivity found for this signal!\n"); + } + + action.first.remove2(sig, &action.second); + } + + ce.assign_map.apply(insig); + ce.assign_map.apply(rstval); + ce.assign_map.apply(sig); + + insig.optimize(); + rstval.optimize(); + sig.optimize(); + + if (sync_always) { + if (sync_edge || sync_level) + log_error("Mixed always event with edge and/or level sensitive events!\n"); + log(" created direct connection (no actual register cell created).\n"); + mod->connections.push_back(RTLIL::SigSig(sig, insig)); + continue; + } + + if (!sync_edge) + log_error("Missing edge-sensitive event for this signal!\n"); + + if (!rstval.is_fully_const() && !ce.eval(rstval)) + log_error("Async reset value `%s' is not constant!\n", log_signal(rstval)); + + gen_dff(mod, insig, rstval.chunks[0].data, sig, + sync_edge->type == RTLIL::SyncType::STp, + sync_level && sync_level->type == RTLIL::SyncType::ST1, + sync_edge->signal, sync_level ? &sync_level->signal : NULL, proc); + } +} + +struct ProcDffPass : public Pass { + ProcDffPass() : Pass("proc_dff") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n"); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) { + ConstEval ce(mod_it.second); + for (auto &proc_it : mod_it.second->processes) + proc_dff(mod_it.second, proc_it.second, ce); + } + } +} ProcDffPass; + diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc new file mode 100644 index 000000000..4d50597d1 --- /dev/null +++ b/passes/proc/proc_mux.cc @@ -0,0 +1,294 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/bitpattern.h" +#include "kernel/log.h" +#include +#include +#include +#include + +static RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) +{ + for (auto &action : cs->actions) { + if (action.first.width) + return action.first; + } + + for (auto sw : cs->switches) + for (auto cs2 : sw->cases) { + RTLIL::SigSpec sig = find_any_lvalue(cs2); + if (sig.width) + return sig; + } + + return RTLIL::SigSpec(); +} + +static void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) +{ + for (auto &action : cs->actions) { + RTLIL::SigSpec lvalue = action.first.extract(sig); + if (lvalue.width) + sig = lvalue; + } + + for (auto sw : cs->switches) + for (auto cs2 : sw->cases) + extract_core_signal(cs2, sig); +} + +static RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SwitchRule *sw) +{ + std::stringstream sstr; + sstr << "$procmux$" << (RTLIL::autoidx++); + + RTLIL::Wire *cmp_wire = new RTLIL::Wire; + cmp_wire->name = sstr.str() + "_CMP"; + cmp_wire->width = 0; + mod->wires[cmp_wire->name] = cmp_wire; + + for (auto comp : compare) + { + RTLIL::SigSpec sig = signal; + sig.expand(); + comp.expand(); + + // get rid of don't-care bits + assert(sig.width == comp.width); + for (int i = 0; i < comp.width; i++) + if (comp.chunks[i].wire == NULL && comp.chunks[i].data.bits[0] == RTLIL::State::Sa) { + sig.remove(i, 1); + comp.remove(i--, 1); + } + if (comp.width == 0) + return RTLIL::SigSpec(); + sig.optimize(); + comp.optimize(); + + if (sig.width == 1 && comp == RTLIL::SigSpec(1,1)) + { + mod->connections.push_back(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, 1, cmp_wire->width++), sig)); + } + else + { + // create compare cell + RTLIL::Cell *eq_cell = new RTLIL::Cell; + std::stringstream sstr2; + sstr2 << sstr.str() << "_CMP" << cmp_wire->width; + eq_cell->name = sstr2.str(); + eq_cell->type = "$eq"; + eq_cell->attributes = sw->attributes; + mod->cells[eq_cell->name] = eq_cell; + + eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); + eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0); + + eq_cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.width); + eq_cell->parameters["\\B_WIDTH"] = RTLIL::Const(comp.width); + eq_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + + eq_cell->connections["\\A"] = sig; + eq_cell->connections["\\B"] = comp; + eq_cell->connections["\\Y"] = RTLIL::SigSpec(cmp_wire, 1, cmp_wire->width++); + } + } + + RTLIL::Wire *ctrl_wire; + if (cmp_wire->width == 1) + { + ctrl_wire = cmp_wire; + } + else + { + ctrl_wire = new RTLIL::Wire; + ctrl_wire->name = sstr.str() + "_CTRL"; + ctrl_wire->width = 1; + mod->wires[ctrl_wire->name] = ctrl_wire; + + // reduce cmp vector to one logic signal + RTLIL::Cell *any_cell = new RTLIL::Cell; + any_cell->name = sstr.str() + "_ANY"; + any_cell->type = "$reduce_or"; + any_cell->attributes = sw->attributes; + mod->cells[any_cell->name] = any_cell; + + any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); + any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width); + any_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); + + any_cell->connections["\\A"] = cmp_wire; + any_cell->connections["\\Y"] = RTLIL::SigSpec(ctrl_wire); + } + + return RTLIL::SigSpec(ctrl_wire); +} + +static RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw) +{ + assert(when_signal.width == else_signal.width); + + std::stringstream sstr; + sstr << "$procmux$" << (RTLIL::autoidx++); + + // the trivial cases + if (compare.size() == 0 || when_signal == else_signal) + return when_signal; + + // compare results + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + if (ctrl_sig.width == 0) + return when_signal; + assert(ctrl_sig.width == 1); + + // prepare multiplexer output signal + RTLIL::Wire *result_wire = new RTLIL::Wire; + result_wire->name = sstr.str() + "_Y"; + result_wire->width = when_signal.width; + mod->wires[result_wire->name] = result_wire; + + // create the multiplexer itself + RTLIL::Cell *mux_cell = new RTLIL::Cell; + mux_cell->name = sstr.str(); + mux_cell->type = "$mux"; + mux_cell->attributes = sw->attributes; + mod->cells[mux_cell->name] = mux_cell; + + mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.width); + mux_cell->connections["\\A"] = else_signal; + mux_cell->connections["\\B"] = when_signal; + mux_cell->connections["\\S"] = ctrl_sig; + mux_cell->connections["\\Y"] = RTLIL::SigSpec(result_wire); + + last_mux_cell = mux_cell; + return RTLIL::SigSpec(result_wire); +} + +static void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw) +{ + assert(last_mux_cell != NULL); + assert(when_signal.width == last_mux_cell->connections["\\A"].width); + + std::stringstream sstr; + sstr << "$procmux$" << (RTLIL::autoidx++); + + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + assert(ctrl_sig.width == 1); + last_mux_cell->type = "$pmux"; + last_mux_cell->connections["\\S"].append(ctrl_sig); + last_mux_cell->connections["\\B"].append(when_signal); + last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->connections["\\S"].width; +} + +static RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) +{ + RTLIL::SigSpec result = defval; + + for (auto &action : cs->actions) { + sig.replace(action.first, action.second, &result); + action.first.remove2(sig, &action.second); + } + + for (auto sw : cs->switches) + { + // detect groups of parallel cases + std::vector pgroups(sw->cases.size()); + if (sw->attributes.count("\\parallel_case") == 0) { + BitPatternPool pool(sw->signal.width); + bool extra_group_for_next_case = false; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + if (i != 0) { + pgroups[i] = pgroups[i-1]; + if (extra_group_for_next_case) { + pgroups[i] = pgroups[i-1]+1; + extra_group_for_next_case = false; + } + for (auto pat : cs2->compare) + if (!pat.is_fully_const() || !pool.has_all(pat)) + pgroups[i] = pgroups[i-1]+1; + if (cs2->compare.empty()) + pgroups[i] = pgroups[i-1]+1; + if (pgroups[i] != pgroups[i-1]) + pool = BitPatternPool(sw->signal.width); + } + for (auto pat : cs2->compare) + if (!pat.is_fully_const()) + extra_group_for_next_case = true; + else + pool.take(pat); + } + } + + // evaluate in reverse order to give the first entry the top priority + RTLIL::SigSpec initial_val = result; + RTLIL::Cell *last_mux_cell = NULL; + for (size_t i = 0; i < sw->cases.size(); i++) { + int case_idx = sw->cases.size() - i - 1; + RTLIL::CaseRule *cs2 = sw->cases[case_idx]; + RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val); + if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); + else + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw); + } + } + + return result; +} + +static void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) +{ + bool first = true; + while (1) + { + RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case); + + if (sig.width == 0) + break; + + if (first) { + log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); + first = false; + } + + extract_core_signal(&proc->root_case, sig); + + log(" creating decoder for signal `%s'.\n", log_signal(sig)); + + RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.width)); + mod->connections.push_back(RTLIL::SigSig(sig, value)); + } +} + +struct ProcMuxPass : public Pass { + ProcMuxPass() : Pass("proc_mux") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + for (auto &proc_it : mod_it.second->processes) + proc_mux(mod_it.second, proc_it.second); + } +} ProcMuxPass; + diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc new file mode 100644 index 000000000..ca2070cad --- /dev/null +++ b/passes/proc/proc_rmdead.cc @@ -0,0 +1,87 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/bitpattern.h" +#include "kernel/log.h" +#include +#include +#include +#include +#include + +static void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) +{ + BitPatternPool pool(sw->signal); + + for (size_t i = 0; i < sw->cases.size(); i++) + { + bool is_default = sw->cases[i]->compare.size() == 0 && !pool.empty(); + + for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) { + RTLIL::SigSpec sig = sw->cases[i]->compare[j]; + if (!sig.is_fully_const()) + continue; + if (!pool.take(sig)) + sw->cases[i]->compare.erase(sw->cases[i]->compare.begin() + (j--)); + } + + if (!is_default) { + if (sw->cases[i]->compare.size() == 0) { + delete sw->cases[i]; + sw->cases.erase(sw->cases.begin() + (i--)); + counter++; + continue; + } + if (pool.empty()) + sw->cases[i]->compare.clear(); + } + + for (auto switch_it : sw->cases[i]->switches) + proc_rmdead(switch_it, counter); + + if (is_default) + pool.take_all(); + } +} + +struct ProcRmdeadPass : public Pass { + ProcRmdeadPass() : Pass("proc_rmdead") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); + + extra_args(args, 1, design); + + int total_counter = 0; + for (auto &mod_it : design->modules) + for (auto &proc_it : mod_it.second->processes) { + int counter = 0; + for (auto switch_it : proc_it.second->root_case.switches) + proc_rmdead(switch_it, counter); + if (counter > 0) + log("Removed %d dead cases from process %s in module %s.\n", counter, + proc_it.first.c_str(), mod_it.first.c_str()); + total_counter += counter; + } + + log("Removed a total of %d dead cases.\n", total_counter); + } +} ProcRmdeadPass; + diff --git a/passes/submod/Makefile.inc b/passes/submod/Makefile.inc new file mode 100644 index 000000000..b0c00eae1 --- /dev/null +++ b/passes/submod/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += passes/submod/submod.o + diff --git a/passes/submod/submod.cc b/passes/submod/submod.cc new file mode 100644 index 000000000..ba1b4b08b --- /dev/null +++ b/passes/submod/submod.cc @@ -0,0 +1,273 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include +#include +#include + +struct SubmodWorker +{ + CellTypes ct; + RTLIL::Design *design; + RTLIL::Module *module; + + struct SubModule + { + std::string name, full_name; + std::set cells; + }; + + std::map submodules; + + struct wire_flags_t { + RTLIL::Wire *new_wire; + bool is_int_driven, is_int_used, is_ext_driven, is_ext_used; + wire_flags_t() : new_wire(NULL), is_int_driven(false), is_int_used(false), is_ext_driven(false), is_ext_used(false) { } + }; + std::map wire_flags; + bool flag_found_something; + + void flag_wire(RTLIL::Wire *wire, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) + { + if (wire_flags.count(wire) == 0) { + if (!create) + return; + wire_flags[wire] = wire_flags_t(); + } + if (set_int_driven) + wire_flags[wire].is_int_driven = true; + if (set_int_used) + wire_flags[wire].is_int_used = true; + if (set_ext_driven) + wire_flags[wire].is_ext_driven = true; + if (set_ext_used) + wire_flags[wire].is_ext_used = true; + flag_found_something = true; + } + + void flag_signal(RTLIL::SigSpec &sig, bool create, bool set_int_driven, bool set_int_used, bool set_ext_driven, bool set_ext_used) + { + for (auto &c : sig.chunks) + if (c.wire != NULL) + flag_wire(c.wire, create, set_int_driven, set_int_used, set_ext_driven, set_ext_used); + } + + void handle_submodule(SubModule &submod) + { + log("Creating submodule %s (%s) of module %s.\n", submod.name.c_str(), submod.full_name.c_str(), module->name.c_str()); + + wire_flags.clear(); + for (RTLIL::Cell *cell : submod.cells) { + if (ct.cell_known(cell->type)) { + for (auto &conn : cell->connections) + flag_signal(conn.second, true, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first), false, false); + } else { + log("WARNING: Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str()); + for (auto &conn : cell->connections) + flag_signal(conn.second, true, true, true, false, false); + } + } + for (auto &it : module->cells) { + RTLIL::Cell *cell = it.second; + if (submod.cells.count(cell) > 0) + continue; + if (ct.cell_known(cell->type)) { + for (auto &conn : cell->connections) + flag_signal(conn.second, false, false, false, ct.cell_output(cell->type, conn.first), ct.cell_input(cell->type, conn.first)); + } else { + flag_found_something = false; + for (auto &conn : cell->connections) + flag_signal(conn.second, false, false, false, true, true); + if (flag_found_something) + log("WARNING: Port directions for cell %s (%s) are unknown. Assuming inout for all ports.\n", cell->name.c_str(), cell->type.c_str()); + } + } + + RTLIL::Module *new_mod = new RTLIL::Module; + new_mod->name = submod.full_name; + design->modules[new_mod->name] = new_mod; + int port_counter = 1, auto_name_counter = 1; + + std::set all_wire_names; + for (auto &it : wire_flags) { + all_wire_names.insert(it.first->name); + } + + for (auto &it : wire_flags) + { + RTLIL::Wire *wire = it.first; + wire_flags_t &flags = it.second; + + if (wire->port_input) + flags.is_ext_driven = true; + if (wire->port_output) + flags.is_ext_used = true; + + RTLIL::Wire *new_wire = new RTLIL::Wire; + new_wire->name = wire->name; + new_wire->width = wire->width; + new_wire->start_offset = wire->start_offset; + new_wire->attributes = wire->attributes; + + if (flags.is_int_driven && flags.is_ext_used) + new_wire->port_output = true; + if (flags.is_ext_driven && flags.is_int_used) + new_wire->port_input = true; + + if (flags.is_int_driven && flags.is_ext_driven) + new_wire->port_input = true, new_wire->port_output = true; + + if (new_wire->port_input || new_wire->port_output) { + new_wire->port_id = port_counter++; + while (new_wire->name[0] == '$') { + std::string new_wire_name = stringf("\\n%d", auto_name_counter++); + if (all_wire_names.count(new_wire_name) == 0) { + all_wire_names.insert(new_wire_name); + new_wire->name = new_wire_name; + } + } + } + + if (new_wire->port_input && new_wire->port_output) + log(" signal %s: inout %s\n", wire->name.c_str(), new_wire->name.c_str()); + else if (new_wire->port_input) + log(" signal %s: input %s\n", wire->name.c_str(), new_wire->name.c_str()); + else if (new_wire->port_output) + log(" signal %s: output %s\n", wire->name.c_str(), new_wire->name.c_str()); + else + log(" signal %s: internal\n", wire->name.c_str()); + + new_mod->wires[new_wire->name] = new_wire; + flags.new_wire = new_wire; + } + + for (RTLIL::Cell *cell : submod.cells) { + RTLIL::Cell *new_cell = new RTLIL::Cell(*cell); + for (auto &conn : new_cell->connections) + for (auto &c : conn.second.chunks) + if (c.wire != NULL) { + assert(wire_flags.count(c.wire) > 0); + c.wire = wire_flags[c.wire].new_wire; + } + log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str()); + new_mod->cells[new_cell->name] = new_cell; + module->cells.erase(cell->name); + delete cell; + } + submod.cells.clear(); + + RTLIL::Cell *new_cell = new RTLIL::Cell; + new_cell->name = submod.full_name; + new_cell->type = submod.full_name; + for (auto &it : wire_flags) + { + RTLIL::Wire *old_wire = it.first; + RTLIL::Wire *new_wire = it.second.new_wire; + if (new_wire->port_id > 0) + new_cell->connections[new_wire->name] = RTLIL::SigSpec(old_wire); + } + module->cells[new_cell->name] = new_cell; + } + + SubmodWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) + { + if (!design->selected_whole_module(module->name)) + return; + + if (module->processes.size() > 0) { + log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str()); + return; + } + + if (module->memories.size() > 0) { + log("Skipping module %s as it contains memories (run 'memory' pass first).\n", module->name.c_str()); + return; + } + + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + for (auto &it : module->wires) + it.second->attributes.erase("\\submod"); + + for (auto &it : module->cells) + { + RTLIL::Cell *cell = it.second; + if (cell->attributes.count("\\submod") == 0 || cell->attributes["\\submod"].str.size() == 0) { + cell->attributes.erase("\\submod"); + continue; + } + + std::string submod_str = cell->attributes["\\submod"].str; + cell->attributes.erase("\\submod"); + + if (submodules.count(submod_str) == 0) { + submodules[submod_str].name = submod_str; + submodules[submod_str].full_name = module->name + "_" + submod_str; + while (design->modules.count(submodules[submod_str].full_name) != 0 || + module->count_id(submodules[submod_str].full_name) != 0) + submodules[submod_str].full_name += "_"; + } + + submodules[submod_str].cells.insert(cell); + } + + for (auto &it : submodules) + handle_submodule(it.second); + } +}; + +struct SubmodPass : public Pass { + SubmodPass() : Pass("submod") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing SUBMOD pass (moving cells to submodes as requested).\n"); + log_push(); + + Pass::call(design, "opt_rmunused"); + log_header("Continuing SUBMOD pass.\n"); + + extra_args(args, 1, design); + + std::set handled_modules; + + bool did_something = true; + while (did_something) { + did_something = false; + std::vector queued_modules; + for (auto &mod_it : design->modules) + if (handled_modules.count(mod_it.first) == 0) + queued_modules.push_back(mod_it.first); + for (auto &modname : queued_modules) + if (design->modules.count(modname) != 0) { + SubmodWorker worker(design, design->modules[modname]); + handled_modules.insert(modname); + did_something = true; + } + } + + Pass::call(design, "opt_rmunused"); + } +} SubmodPass; + diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc new file mode 100644 index 000000000..ba365525d --- /dev/null +++ b/passes/techmap/Makefile.inc @@ -0,0 +1,11 @@ + +GENFILES += passes/techmap/stdcells.inc +OBJS += passes/techmap/techmap.o + +passes/techmap/stdcells.inc: techlibs/stdcells.v + od -v -td1 -w1 $< | awk 'BEGIN { print "static char stdcells_code[] = {"; } $$2 != "" { print $$2 ","; } \ + END { print 0 "};"; }' | fmt > $@.new + mv $@.new $@ + +passes/techmap/techmap.o: passes/techmap/stdcells.inc + diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc new file mode 100644 index 000000000..99fa15b92 --- /dev/null +++ b/passes/techmap/techmap.cc @@ -0,0 +1,207 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/log.h" +#include +#include +#include +#include + +#include "passes/techmap/stdcells.inc" + +static void apply_prefix(std::string prefix, std::string &id) +{ + if (id[0] == '\\') + id = prefix + "." + id.substr(1); + else + id = prefix + "." + id; +} + +static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) +{ + for (size_t i = 0; i < sig.chunks.size(); i++) { + if (sig.chunks[i].wire == NULL) + continue; + std::string wire_name = sig.chunks[i].wire->name; + apply_prefix(prefix, wire_name); + assert(module->wires.count(wire_name) > 0); + sig.chunks[i].wire = module->wires[wire_name]; + } +} + +std::map>, RTLIL::Module*> techmap_cache; + +static bool techmap_module(RTLIL::Module *module, RTLIL::Design *map) +{ + bool did_something = false; + + std::vector cell_names; + + for (auto &cell_it : module->cells) + cell_names.push_back(cell_it.first); + + for (auto &cell_name : cell_names) + { + if (module->cells.count(cell_name) == 0) + continue; + + RTLIL::Cell *cell = module->cells[cell_name]; + + if (map->modules.count(cell->type) == 0) + continue; + + RTLIL::Module *tpl = map->modules[cell->type]; + std::pair> key(cell->type, cell->parameters); + + if (techmap_cache.count(key) > 0) { + tpl = techmap_cache[key]; + } else { + std::string derived_name = cell->type; + if (cell->parameters.size() != 0) { + derived_name = tpl->derive(map, cell->parameters); + tpl = map->modules[derived_name]; + log_header("Continuing TECHMAP pass.\n"); + } + for (auto &cit : tpl->cells) + if (cit.second->type == "\\TECHMAP_FAILED") { + log("Not using module `%s' from techmap as it contains a TECHMAP_FAILED marker cell.\n", derived_name.c_str()); + tpl = NULL; + break; + } + techmap_cache[key] = tpl; + } + + if (tpl == NULL) + goto next_cell; + + log("Mapping `%s.%s' using `%s'.\n", module->name.c_str(), cell_name.c_str(), tpl->name.c_str()); + + if (tpl->memories.size() != 0) + log_error("Technology map yielded memories -> this is not supported."); + + if (tpl->processes.size() != 0) + log_error("Technology map yielded processes -> this is not supported."); + + for (auto &it : tpl->wires) { + RTLIL::Wire *w = new RTLIL::Wire(*it.second); + apply_prefix(cell_name, w->name); + w->port_input = false; + w->port_output = false; + w->port_id = 0; + module->wires[w->name] = w; + } + + for (auto &it : tpl->cells) { + RTLIL::Cell *c = new RTLIL::Cell(*it.second); + if (c->type.substr(0, 2) == "\\$") + c->type = c->type.substr(1); + apply_prefix(cell_name, c->name); + for (auto &it2 : c->connections) + apply_prefix(cell_name, it2.second, module); + module->cells[c->name] = c; + } + + for (auto &it : tpl->connections) { + RTLIL::SigSig c = it; + apply_prefix(cell_name, c.first, module); + apply_prefix(cell_name, c.second, module); + module->connections.push_back(c); + } + + for (auto &it : cell->connections) { + assert(tpl->wires.count(it.first)); + assert(tpl->wires[it.first]->port_id > 0); + RTLIL::Wire *w = tpl->wires[it.first]; + RTLIL::SigSig c; + if (w->port_output) { + c.first = it.second; + c.second = RTLIL::SigSpec(w); + apply_prefix(cell_name, c.second, module); + } else { + c.first = RTLIL::SigSpec(w); + c.second = it.second; + apply_prefix(cell_name, c.first, module); + } + if (c.second.width > c.first.width) + c.second.remove(c.first.width, c.second.width - c.first.width); + if (c.second.width < c.first.width) + c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.width - c.second.width)); + assert(c.first.width == c.second.width); + module->connections.push_back(c); + } + + delete cell; + module->cells.erase(cell_name); + did_something = true; + next_cell:; + } + + return did_something; +} + +struct TechmapPass : public Pass { + TechmapPass() : Pass("techmap") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing TECHMAP pass (map to technology primitives).\n"); + log_push(); + + std::string filename; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-map" && argidx+1 < args.size()) { + filename = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + RTLIL::Design *map = new RTLIL::Design; + FILE *f = filename.empty() ? fmemopen(stdcells_code, strlen(stdcells_code), "rt") : fopen(filename.c_str(), "rt"); + if (f == NULL) + log_error("Can't open map file `%s'\n", filename.c_str()); + Frontend::frontend_call(map, f, filename.empty() ? "" : filename, "verilog"); + fclose(f); + + std::map modules_new; + for (auto &it : map->modules) { + if (it.first.substr(0, 2) == "\\$") + it.second->name = it.first.substr(1); + modules_new[it.second->name] = it.second; + } + map->modules.swap(modules_new); + + bool did_something = true; + while (did_something) { + did_something = false; + for (auto &mod_it : design->modules) + if (techmap_module(mod_it.second, map)) + did_something = true; + } + + log("No more expansions possible.\n"); + techmap_cache.clear(); + delete map; + log_pop(); + } +} TechmapPass; + diff --git a/techlibs/Makefile.inc b/techlibs/Makefile.inc new file mode 100644 index 000000000..031a4ad30 --- /dev/null +++ b/techlibs/Makefile.inc @@ -0,0 +1,7 @@ + +TARGETS += techlibs/blackbox.v + +techlibs/blackbox.v: techlibs/blackbox.sed techlibs/simlib.v techlibs/stdcells_sim.v + cat techlibs/simlib.v techlibs/stdcells_sim.v | sed -rf techlibs/blackbox.sed > techlibs/blackbox.v.new + mv techlibs/blackbox.v.new techlibs/blackbox.v + diff --git a/techlibs/blackbox.sed b/techlibs/blackbox.sed new file mode 100644 index 000000000..4e9a3a7c6 --- /dev/null +++ b/techlibs/blackbox.sed @@ -0,0 +1,4 @@ +#!/bin/sed -r +/^(wire|assign|reg)/ d; +/^(genvar|always|initial)/,/^end/ d; +s/ reg / /; diff --git a/techlibs/simlib.v b/techlibs/simlib.v new file mode 100644 index 000000000..179242541 --- /dev/null +++ b/techlibs/simlib.v @@ -0,0 +1,892 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Simulation Library. + * + * This verilog library contains simple simulation models for the internal + * cells ($not, ...) generated by the frontends and used in most passes. + * + * This library can be used to verify the internal netlists as generated + * by the different frontends and passes. + * + * Note that memory can only be simulated when all $memrd and $memwr cells + * have been merged to stand-alone $mem cells (this is what the "memory_collect" + * pass is doing). + * + */ + +`define INPUT_A \ +input [A_WIDTH-1:0] A; \ +generate if (A_SIGNED) begin:A_BUF wire signed [A_WIDTH-1:0] val = A; end else begin:A_BUF wire [A_WIDTH-1:0] val = A; end endgenerate + +`define INPUT_B \ +input [B_WIDTH-1:0] B; \ +generate if (B_SIGNED) begin:B_BUF wire signed [B_WIDTH-1:0] val = B; end else begin:B_BUF wire [B_WIDTH-1:0] val = B; end endgenerate + +// -------------------------------------------------------- + +module \$not (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output [Y_WIDTH-1:0] Y; + +assign Y = ~A_BUF.val; + +endmodule + + +// -------------------------------------------------------- + +module \$pos (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output [Y_WIDTH-1:0] Y; + +assign Y = +A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$neg (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output [Y_WIDTH-1:0] Y; + +assign Y = -A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$and (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val & B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$or (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val | B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$xor (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val ^ B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$xnor (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val ~^ B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_and (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output Y; + +assign Y = &A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_or (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output Y; + +assign Y = |A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_xor (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output Y; + +assign Y = ^A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_xnor (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output Y; + +assign Y = ~^A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_bool (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output Y; + +assign Y = A_BUF.val != 0; + +endmodule + +// -------------------------------------------------------- + +module \$shl (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val << B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$shr (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val >> B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$sshl (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val <<< B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$sshr (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val >>> B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$lt (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val < B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$le (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val <= B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$eq (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val == B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$ne (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val != B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$ge (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val >= B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$gt (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val > B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$add (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val + B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$sub (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val - B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$mul (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val * B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$div (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val / B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$mod (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val % B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$pow (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val ** B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$logic_not (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +output [Y_WIDTH-1:0] Y; + +assign Y = !A_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$logic_and (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val && B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$logic_or (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +`INPUT_A +`INPUT_B +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val || B_BUF.val; + +endmodule + +// -------------------------------------------------------- + +module \$mux (A, B, S, Y); + +parameter WIDTH = 0; + +input [WIDTH-1:0] A, B; +input S; +output reg [WIDTH-1:0] Y; + +always @* begin + if (S) + Y = B; + else + Y = A; +end + +endmodule + +// -------------------------------------------------------- + +module \$pmux (A, B, S, Y); + +parameter WIDTH = 0; +parameter S_WIDTH = 0; + +input [WIDTH-1:0] A; +input [WIDTH*S_WIDTH-1:0] B; +input [S_WIDTH-1:0] S; +output reg [WIDTH-1:0] Y; + +integer i; + +always @* begin + Y = A; + for (i = 0; i < S_WIDTH; i = i+1) + if (S[i]) + Y = B >> (WIDTH*i); +end + +endmodule + +// -------------------------------------------------------- + +module \$safe_pmux (A, B, S, Y); + +parameter WIDTH = 0; +parameter S_WIDTH = 0; + +input [WIDTH-1:0] A; +input [WIDTH*S_WIDTH-1:0] B; +input [S_WIDTH-1:0] S; +output reg [WIDTH-1:0] Y; + +integer i, j; + +always @* begin + j = 0; + for (i = 0; i < S_WIDTH; i = i+1) + if (S[i]) begin + Y = B >> (WIDTH*i); + j = j + 1; + end + if (j != 1) + Y = A; +end + +endmodule + +// -------------------------------------------------------- + +module \$dff (CLK, D, Q); + +parameter WIDTH = 0; +parameter CLK_POLARITY = 1'b1; + +input CLK; +input [WIDTH-1:0] D; +output reg [WIDTH-1:0] Q; +wire pos_clk = CLK == CLK_POLARITY; + +always @(posedge pos_clk) begin + Q <= D; +end + +endmodule + +// -------------------------------------------------------- + +module \$adff (CLK, ARST, D, Q); + +parameter WIDTH = 0; +parameter CLK_POLARITY = 1'b1; +parameter ARST_POLARITY = 1'b1; +parameter ARST_VALUE = 0; + +input CLK, ARST; +input [WIDTH-1:0] D; +output reg [WIDTH-1:0] Q; +wire pos_clk = CLK == CLK_POLARITY; +wire pos_arst = ARST == ARST_POLARITY; + +always @(posedge pos_clk, posedge pos_arst) begin + if (pos_arst) + Q <= ARST_VALUE; + else + Q <= D; +end + +endmodule + +// -------------------------------------------------------- + +module \$fsm (CLK, ARST, CTRL_IN, CTRL_OUT); + +parameter NAME = ""; + +parameter CLK_POLARITY = 1'b1; +parameter ARST_POLARITY = 1'b1; + +parameter CTRL_IN_WIDTH = 1; +parameter CTRL_OUT_WIDTH = 1; + +parameter STATE_BITS = 1; +parameter STATE_NUM = 1; +parameter STATE_NUM_LOG2 = 1; +parameter STATE_RST = 0; +parameter STATE_TABLE = 1'b0; + +parameter TRANS_NUM = 1; +parameter TRANS_TABLE = 4'b0x0x; + +input CLK, ARST; +input [CTRL_IN_WIDTH-1:0] CTRL_IN; +output reg [CTRL_OUT_WIDTH-1:0] CTRL_OUT; + +wire pos_clk = CLK == CLK_POLARITY; +wire pos_arst = ARST == ARST_POLARITY; + +reg [STATE_BITS-1:0] state; +reg [STATE_BITS-1:0] state_tmp; +reg [STATE_BITS-1:0] next_state; + +reg [STATE_BITS-1:0] tr_state_in; +reg [STATE_BITS-1:0] tr_state_out; +reg [CTRL_IN_WIDTH-1:0] tr_ctrl_in; +reg [CTRL_OUT_WIDTH-1:0] tr_ctrl_out; + +integer i; + +task tr_fetch; + input [31:0] tr_num; + reg [31:0] tr_pos; + reg [STATE_NUM_LOG2-1:0] state_num; + begin + tr_pos = (2*STATE_NUM_LOG2+CTRL_IN_WIDTH+CTRL_OUT_WIDTH)*tr_num; + tr_ctrl_out = TRANS_TABLE >> tr_pos; + tr_pos = tr_pos + CTRL_OUT_WIDTH; + state_num = TRANS_TABLE >> tr_pos; + tr_state_out = STATE_TABLE >> (STATE_BITS*state_num); + tr_pos = tr_pos + STATE_NUM_LOG2; + tr_ctrl_in = TRANS_TABLE >> tr_pos; + tr_pos = tr_pos + CTRL_IN_WIDTH; + state_num = TRANS_TABLE >> tr_pos; + tr_state_in = STATE_TABLE >> (STATE_BITS*state_num); + tr_pos = tr_pos + STATE_NUM_LOG2; + end +endtask + +always @(posedge pos_clk, posedge pos_arst) begin + if (pos_arst) + state_tmp = STATE_TABLE[STATE_BITS*(STATE_RST+1)-1:STATE_BITS*STATE_RST]; + else + state_tmp = next_state; + for (i = 0; i < STATE_BITS; i = i+1) + if (state_tmp[i] === 1'bz) + state_tmp[i] = 0; + state <= state_tmp; +end + +always @(state, CTRL_IN) begin + next_state <= STATE_TABLE[STATE_BITS*(STATE_RST+1)-1:STATE_BITS*STATE_RST]; + CTRL_OUT <= 'bx; + // $display("---"); + // $display("Q: %b %b", state, CTRL_IN); + for (i = 0; i < TRANS_NUM; i = i+1) begin + tr_fetch(i); + // $display("T: %b %b -> %b %b [%d]", tr_state_in, tr_ctrl_in, tr_state_out, tr_ctrl_out, i); + casez ({state, CTRL_IN}) + {tr_state_in, tr_ctrl_in}: begin + // $display("-> %b %b <- MATCH", state, CTRL_IN); + {next_state, CTRL_OUT} <= {tr_state_out, tr_ctrl_out}; + end + endcase + end +end + +endmodule + +// -------------------------------------------------------- +`ifndef SIMLIB_NOMEM + +module \$memrd (CLK, ADDR, DATA); + +parameter MEMID = ""; +parameter ABITS = 8; +parameter WIDTH = 8; + +parameter RD_CLK_ENABLE = 0; +parameter RD_CLK_POLARITY = 0; + +input CLK; +input [ABITS-1:0] ADDR; +output [WIDTH-1:0] DATA; + +initial begin + $display("ERROR: Found non-simulatable instance of $memrd!"); + $finish; +end + +endmodule + +// -------------------------------------------------------- + +module \$memwr (CLK, EN, ADDR, DATA); + +parameter MEMID = ""; +parameter ABITS = 8; +parameter WIDTH = 8; + +parameter RD_CLK_ENABLE = 0; +parameter RD_CLK_POLARITY = 0; + +input CLK, EN; +input [ABITS-1:0] ADDR; +input [WIDTH-1:0] DATA; + +initial begin + $display("ERROR: Found non-simulatable instance of $memwr!"); + $finish; +end + +endmodule + +// -------------------------------------------------------- + +module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); + +parameter MEMID = ""; +parameter SIZE = 256; +parameter ABITS = 8; +parameter WIDTH = 8; + +parameter RD_PORTS = 1; +parameter RD_CLK_ENABLE = 1'b1; +parameter RD_CLK_POLARITY = 1'b1; + +parameter WR_PORTS = 1; +parameter WR_CLK_ENABLE = 1'b1; +parameter WR_CLK_POLARITY = 1'b1; + +input [RD_PORTS-1:0] RD_CLK; +input [RD_PORTS*ABITS-1:0] RD_ADDR; +output reg [RD_PORTS*WIDTH-1:0] RD_DATA; + +input [WR_PORTS-1:0] WR_CLK, WR_EN; +input [WR_PORTS*ABITS-1:0] WR_ADDR; +input [WR_PORTS*WIDTH-1:0] WR_DATA; + +reg [WIDTH-1:0] data [SIZE-1:0]; +event update_async_rd; + +genvar i; +generate + + for (i = 0; i < RD_PORTS; i = i+1) begin:rd + if (RD_CLK_ENABLE[i] == 0) begin:rd_noclk + always @(RD_ADDR or update_async_rd) + RD_DATA[ (i+1)*WIDTH-1 : i*WIDTH ] <= data[ RD_ADDR[ (i+1)*ABITS-1 : i*ABITS ] ]; + end else + if (RD_CLK_POLARITY[i] == 1) begin:rd_posclk + always @(posedge RD_CLK[i]) + RD_DATA[ (i+1)*WIDTH-1 : i*WIDTH ] <= data[ RD_ADDR[ (i+1)*ABITS-1 : i*ABITS ] ]; + end else begin:rd_negclk + always @(negedge RD_CLK[i]) + RD_DATA[ (i+1)*WIDTH-1 : i*WIDTH ] <= data[ RD_ADDR[ (i+1)*ABITS-1 : i*ABITS ] ]; + end + end + + for (i = 0; i < WR_PORTS; i = i+1) begin:wr + if (WR_CLK_ENABLE[i] == 0) begin:wr_noclk + always @(WR_ADDR or WR_DATA or WR_EN) begin + if (WR_EN[i]) begin + data[ WR_ADDR[ (i+1)*ABITS-1 : i*ABITS ] ] <= WR_DATA[ (i+1)*WIDTH-1 : i*WIDTH ]; + #1 -> update_async_rd; + end + end + end else + if (RD_CLK_POLARITY[i] == 1) begin:rd_posclk + always @(posedge WR_CLK[i]) + if (WR_EN[i]) begin + data[ WR_ADDR[ (i+1)*ABITS-1 : i*ABITS ] ] <= WR_DATA[ (i+1)*WIDTH-1 : i*WIDTH ]; + #1 -> update_async_rd; + end + end else begin:rd_negclk + always @(negedge WR_CLK[i]) + if (WR_EN[i]) begin + data[ WR_ADDR[ (i+1)*ABITS-1 : i*ABITS ] ] <= WR_DATA[ (i+1)*WIDTH-1 : i*WIDTH ]; + #1 -> update_async_rd; + end + end + end + +endgenerate + +endmodule + +`endif +// -------------------------------------------------------- diff --git a/techlibs/stdcells.v b/techlibs/stdcells.v new file mode 100644 index 000000000..9733c370c --- /dev/null +++ b/techlibs/stdcells.v @@ -0,0 +1,1378 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The internal logic cell technology mapper. + * + * This verilog library contains the mapping of internal cells (e.g. $not with + * variable bit width) to the internal logic cells (such as the single bit $_INV_ + * gate). Usually this logic network is then mapped to the actual technology + * using e.g. the "abc" pass. + * + * Note that this library does not map $mem cells. They must be mapped to logic + * and $dff cells using the "memory_map" pass first. (Or map it to custom cells, + * which is of course highly recommended for larger memories.) + * + */ + +// -------------------------------------------------------- + +module \$not (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output [Y_WIDTH-1:0] Y; + +genvar i; +generate + for (i = 0; i < Y_WIDTH; i = i + 1) begin:V + if (i < A_WIDTH) begin + \$_INV_ gate ( + .A(A[i]), + .Y(Y[i]) + ); + end else begin + assign Y[i] = 0; + end + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$pos (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output [Y_WIDTH-1:0] Y; + +genvar i; +generate + for (i = 0; i < Y_WIDTH; i = i + 1) begin:V + if (i < A_WIDTH) begin + assign Y[i] = A[i]; + end else if (A_SIGNED) begin + assign Y[i] = A[A_WIDTH-1]; + end else begin + assign Y[i] = 0; + end + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$neg (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output [Y_WIDTH-1:0] Y; + +\$sub #( + .A_SIGNED(A_SIGNED), + .B_SIGNED(A_SIGNED), + .A_WIDTH(1), + .B_WIDTH(A_WIDTH), + .Y_WIDTH(Y_WIDTH) +) sub ( + .A(0), + .B(A), + .Y(Y) +); + +endmodule + +// -------------------------------------------------------- + +module \$and (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire [Y_WIDTH-1:0] A_buf, B_buf; +\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + +genvar i; +generate + for (i = 0; i < Y_WIDTH; i = i + 1) begin:V + \$_AND_ gate ( + .A(A_buf[i]), + .B(B_buf[i]), + .Y(Y[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$or (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire [Y_WIDTH-1:0] A_buf, B_buf; +\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + +genvar i; +generate + for (i = 0; i < Y_WIDTH; i = i + 1) begin:V + \$_OR_ gate ( + .A(A_buf[i]), + .B(B_buf[i]), + .Y(Y[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$xor (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire [Y_WIDTH-1:0] A_buf, B_buf; +\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + +genvar i; +generate + for (i = 0; i < Y_WIDTH; i = i + 1) begin:V + \$_XOR_ gate ( + .A(A_buf[i]), + .B(B_buf[i]), + .Y(Y[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$xnor (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire [Y_WIDTH-1:0] A_buf, B_buf; +\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + +genvar i; +generate + for (i = 0; i < Y_WIDTH; i = i + 1) begin:V + wire tmp; + \$_XOR_ gate1 ( + .A(A_buf[i]), + .B(B_buf[i]), + .Y(tmp) + ); + \$_INV_ gate2 ( + .A(tmp), + .Y(Y[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$reduce_and (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output Y; + +wire [A_WIDTH-1:0] buffer; + +genvar i; +generate + for (i = 1; i < A_WIDTH; i = i + 1) begin:V + \$_AND_ gate ( + .A(A[i]), + .B(buffer[i-1]), + .Y(buffer[i]) + ); + end +endgenerate + +assign buffer[0] = A[0]; +assign Y = buffer[A_WIDTH-1]; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_or (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output Y; + +wire [A_WIDTH-1:0] buffer; + +genvar i; +generate + for (i = 1; i < A_WIDTH; i = i + 1) begin:V + \$_OR_ gate ( + .A(A[i]), + .B(buffer[i-1]), + .Y(buffer[i]) + ); + end +endgenerate + +assign buffer[0] = A[0]; +assign Y = buffer[A_WIDTH-1]; + +endmodule + +// -------------------------------------------------------- + +module \$reduce_xor (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output Y; + +wire [A_WIDTH-1:0] buffer; + +genvar i; +generate + for (i = 1; i < A_WIDTH; i = i + 1) begin:V + \$_XOR_ gate ( + .A(A[i]), + .B(buffer[i-1]), + .Y(buffer[i]) + ); + end +endgenerate + +assign buffer[0] = A[0]; +assign Y = buffer[A_WIDTH-1]; + +endmodule + + +// -------------------------------------------------------- + +module \$reduce_xnor (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output Y; + +wire [A_WIDTH-1:0] buffer; + +genvar i; +generate + for (i = 1; i < A_WIDTH; i = i + 1) begin:V + \$_XOR_ gate ( + .A(A[i]), + .B(buffer[i-1]), + .Y(buffer[i]) + ); + end +endgenerate + +assign buffer[0] = A[0]; + \$_INV_ gate_inv ( + .A(buffer[A_WIDTH-1]), + .Y(Y) +); + +endmodule + +// -------------------------------------------------------- + +module \$reduce_bool (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output Y; + +wire [A_WIDTH-1:0] buffer; + +genvar i; +generate + for (i = 1; i < A_WIDTH; i = i + 1) begin:V + \$_OR_ gate ( + .A(A[i]), + .B(buffer[i-1]), + .Y(buffer[i]) + ); + end +endgenerate + +assign buffer[0] = A[0]; +assign Y = buffer[A_WIDTH-1]; + +endmodule + +// -------------------------------------------------------- + +module \$shift (X, A, Y); + +parameter WIDTH = 1; +parameter SHIFT = 0; + +input X; +input [WIDTH-1:0] A; +output [WIDTH-1:0] Y; + +genvar i; +generate + for (i = 0; i < WIDTH; i = i + 1) begin:V + if (i+SHIFT < 0) begin + assign Y[i] = 0; + end else + if (i+SHIFT < WIDTH) begin + assign Y[i] = A[i+SHIFT]; + end else begin + assign Y[i] = X; + end + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$shl (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = Y_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +genvar i; +generate + wire [WIDTH*(B_WIDTH+1)-1:0] chain; + \$pos #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH), + .Y_WIDTH(WIDTH) + ) expand ( + .A(A), + .Y(chain[WIDTH-1:0]) + ); + assign Y = chain[WIDTH*(B_WIDTH+1)-1 : WIDTH*B_WIDTH]; + for (i = 0; i < B_WIDTH; i = i + 1) begin:V + wire [WIDTH-1:0] unshifted, shifted, result; + assign unshifted = chain[WIDTH*i + WIDTH-1 : WIDTH*i]; + assign chain[WIDTH*(i+1) + WIDTH-1 : WIDTH*(i+1)] = result; + \$shift #( + .WIDTH(WIDTH), + .SHIFT(0 - (2 ** i)) + ) sh ( + .X(0), + .A(unshifted), + .Y(shifted) + ); + \$mux #( + .WIDTH(WIDTH) + ) mux ( + .A(unshifted), + .B(shifted), + .Y(result), + .S(B[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$shr (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = A_WIDTH > Y_WIDTH ? A_WIDTH : Y_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +genvar i; +generate + wire [WIDTH*(B_WIDTH+1)-1:0] chain; + \$pos #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH), + .Y_WIDTH(WIDTH) + ) expand ( + .A(A), + .Y(chain[WIDTH-1:0]) + ); + assign Y = chain[WIDTH*(B_WIDTH+1)-1 : WIDTH*B_WIDTH]; + for (i = 0; i < B_WIDTH; i = i + 1) begin:V + wire [WIDTH-1:0] unshifted, shifted, result; + assign unshifted = chain[WIDTH*i + WIDTH-1 : WIDTH*i]; + assign chain[WIDTH*(i+1) + WIDTH-1 : WIDTH*(i+1)] = result; + \$shift #( + .WIDTH(WIDTH), + .SHIFT(2 ** i) + ) sh ( + .X(0), + .A(unshifted), + .Y(shifted) + ); + \$mux #( + .WIDTH(WIDTH) + ) mux ( + .A(unshifted), + .B(shifted), + .Y(result), + .S(B[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$sshl (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = Y_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +genvar i; +generate + wire [WIDTH*(B_WIDTH+1)-1:0] chain; + \$pos #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH), + .Y_WIDTH(WIDTH) + ) expand ( + .A(A), + .Y(chain[WIDTH-1:0]) + ); + assign Y = chain[WIDTH*(B_WIDTH+1)-1 : WIDTH*B_WIDTH]; + for (i = 0; i < B_WIDTH; i = i + 1) begin:V + wire [WIDTH-1:0] unshifted, shifted, result; + assign unshifted = chain[WIDTH*i + WIDTH-1 : WIDTH*i]; + assign chain[WIDTH*(i+1) + WIDTH-1 : WIDTH*(i+1)] = result; + \$shift #( + .WIDTH(WIDTH), + .SHIFT(0 - (2 ** i)) + ) sh ( + .X(0), + .A(unshifted), + .Y(shifted) + ); + \$mux #( + .WIDTH(WIDTH) + ) mux ( + .A(unshifted), + .B(shifted), + .Y(result), + .S(B[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$sshr (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = A_WIDTH > Y_WIDTH ? A_WIDTH : Y_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +genvar i; +generate + wire [WIDTH*(B_WIDTH+1)-1:0] chain; + \$pos #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH), + .Y_WIDTH(WIDTH) + ) expand ( + .A(A), + .Y(chain[WIDTH-1:0]) + ); + for (i = 0; i < Y_WIDTH; i = i + 1) begin:Y + if (i < WIDTH) begin + assign Y[i] = chain[WIDTH*B_WIDTH + i]; + end else + if (A_SIGNED) begin + assign Y[i] = chain[WIDTH*B_WIDTH + WIDTH-1]; + end else begin + assign Y[i] = 0; + end + end + for (i = 0; i < B_WIDTH; i = i + 1) begin:V + wire [WIDTH-1:0] unshifted, shifted, result; + assign unshifted = chain[WIDTH*i + WIDTH-1 : WIDTH*i]; + assign chain[WIDTH*(i+1) + WIDTH-1 : WIDTH*(i+1)] = result; + \$shift #( + .WIDTH(WIDTH), + .SHIFT(2 ** i) + ) sh ( + .X(A_SIGNED && A[A_WIDTH-1]), + .A(unshifted), + .Y(shifted) + ); + \$mux #( + .WIDTH(WIDTH) + ) mux ( + .A(unshifted), + .B(shifted), + .Y(result), + .S(B[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$fulladd (A, B, C, X, Y); + +// {X, Y} = A + B + C +input A, B, C; +output X, Y; + +// {t1, t2} = A + B +wire t1, t2, t3; + + \$_AND_ gate1 ( .A(A), .B(B), .Y(t1) ); + \$_XOR_ gate2 ( .A(A), .B(B), .Y(t2) ); + \$_AND_ gate3 ( .A(t2), .B(C), .Y(t3) ); + \$_XOR_ gate4 ( .A(t2), .B(C), .Y(Y) ); + \$_OR_ gate5 ( .A(t1), .B(t3), .Y(X) ); + +endmodule + + +// -------------------------------------------------------- + +module \$alu (A, B, Cin, Y, Cout, Csign); + +parameter WIDTH = 1; + +input [WIDTH-1:0] A, B; +input Cin; + +output [WIDTH-1:0] Y; +output Cout, Csign; + +wire [WIDTH:0] carry; +assign carry[0] = Cin; +assign Cout = carry[WIDTH]; +assign Csign = carry[WIDTH-1]; + +genvar i; +generate + for (i = 0; i < WIDTH; i = i + 1) begin:V + \$fulladd adder ( + .A(A[i]), + .B(B[i]), + .C(carry[i]), + .X(carry[i+1]), + .Y(Y[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$lt (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = A_WIDTH > B_WIDTH ? A_WIDTH : B_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output Y; + +wire carry, carry_sign; +wire [WIDTH-1:0] A_buf, B_buf, Y_buf; +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf)); + +\$alu #( + .WIDTH(WIDTH) +) alu ( + .A(A_buf), + .B(~B_buf), + .Cin(1'b1), + .Y(Y_buf), + .Cout(carry), + .Csign(carry_sign), +); + +// ALU flags +wire cf, of, zf, sf; +assign cf = !carry; +assign of = carry ^ carry_sign; +assign zf = ~|Y_buf; +assign sf = Y_buf[WIDTH-1]; + +generate + if (A_SIGNED && B_SIGNED) begin + assign Y = of != sf; + end else begin + assign Y = cf; + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$le (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = A_WIDTH > B_WIDTH ? A_WIDTH : B_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output Y; + +wire carry, carry_sign; +wire [WIDTH-1:0] A_buf, B_buf, Y_buf; +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf)); + +\$alu #( + .WIDTH(WIDTH) +) alu ( + .A(A_buf), + .B(~B_buf), + .Cin(1'b1), + .Y(Y_buf), + .Cout(carry), + .Csign(carry_sign), +); + +// ALU flags +wire cf, of, zf, sf; +assign cf = !carry; +assign of = carry ^ carry_sign; +assign zf = ~|Y_buf; +assign sf = Y_buf[WIDTH-1]; + +generate + if (A_SIGNED && B_SIGNED) begin + assign Y = zf || (of != sf); + end else begin + assign Y = zf || cf; + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$eq (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = A_WIDTH > B_WIDTH ? A_WIDTH : B_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output Y; + +wire carry, carry_sign; +wire [WIDTH-1:0] A_buf, B_buf, Y_buf; +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf)); + +assign Y = ~|(A_buf ^ B_buf); + +endmodule + +// -------------------------------------------------------- + +module \$ne (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +parameter WIDTH = A_WIDTH > B_WIDTH ? A_WIDTH : B_WIDTH; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output Y; + +wire carry, carry_sign; +wire [WIDTH-1:0] A_buf, B_buf, Y_buf; +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf)); + +assign Y = |(A_buf ^ B_buf); + +endmodule + +// -------------------------------------------------------- + +module \$ge (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output Y; + +\$le #( + .A_SIGNED(B_SIGNED), + .B_SIGNED(A_SIGNED), + .A_WIDTH(B_WIDTH), + .B_WIDTH(A_WIDTH) +) ge_via_le ( + .A(B), + .B(A), + .Y(Y) +); + +endmodule + +// -------------------------------------------------------- + +module \$gt (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output Y; + +\$lt #( + .A_SIGNED(B_SIGNED), + .B_SIGNED(A_SIGNED), + .A_WIDTH(B_WIDTH), + .B_WIDTH(A_WIDTH) +) gt_via_lt ( + .A(B), + .B(A), + .Y(Y) +); + +endmodule + +// -------------------------------------------------------- + +module \$add (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire [Y_WIDTH-1:0] A_buf, B_buf; +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + +\$alu #( + .WIDTH(Y_WIDTH) +) alu ( + .A(A_buf), + .B(B_buf), + .Cin(1'b0), + .Y(Y) +); + +endmodule + +// -------------------------------------------------------- + +module \$sub (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire [Y_WIDTH-1:0] A_buf, B_buf; +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); +\$pos #(.A_SIGNED(A_SIGNED && B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + +\$alu #( + .WIDTH(Y_WIDTH) +) alu ( + .A(A_buf), + .B(~B_buf), + .Cin(1'b1), + .Y(Y) +); + +endmodule + +/**** +// -------------------------------------------------------- + +module \$mul (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire signed [A_WIDTH:0] buffer_a = A_SIGNED ? $signed(A) : A; +wire signed [B_WIDTH:0] buffer_b = B_SIGNED ? $signed(B) : B; + +assign Y = buffer_a * buffer_b; + +endmodule + +// -------------------------------------------------------- + +module \$div (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire signed [A_WIDTH:0] buffer_a = A_SIGNED ? $signed(A) : A; +wire signed [B_WIDTH:0] buffer_b = B_SIGNED ? $signed(B) : B; + +assign Y = buffer_a / buffer_b; + +endmodule + +// -------------------------------------------------------- + +module \$mod (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire signed [A_WIDTH:0] buffer_a = A_SIGNED ? $signed(A) : A; +wire signed [B_WIDTH:0] buffer_b = B_SIGNED ? $signed(B) : B; + +assign Y = buffer_a % buffer_b; + +endmodule + +// -------------------------------------------------------- + +module \$pow (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire signed [A_WIDTH:0] buffer_a = A_SIGNED ? $signed(A) : A; +wire signed [B_WIDTH:0] buffer_b = B_SIGNED ? $signed(B) : B; + +assign Y = buffer_a ** buffer_b; + +endmodule + +// -------------------------------------------------------- +****/ + +module \$logic_not (A, Y); + +parameter A_SIGNED = 0; +parameter A_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +output [Y_WIDTH-1:0] Y; + +wire A_buf; + +\$reduce_bool #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH) +) A_logic ( + .A(A), + .Y(A_buf) +); + + \$_INV_ gate ( + .A(A_buf), + .Y(Y[0]) +); + +generate + if (Y_WIDTH > 1) begin:V + assign Y[Y_WIDTH-1:1] = 0; + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$logic_and (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire A_buf, B_buf; + +\$reduce_bool #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH) +) A_logic ( + .A(A), + .Y(A_buf) +); + +\$reduce_bool #( + .A_SIGNED(B_SIGNED), + .A_WIDTH(B_WIDTH) +) B_logic ( + .A(B), + .Y(B_buf) +); + + \$_AND_ gate ( + .A(A_buf), + .B(B_buf), + .Y(Y[0]) +); + +generate + if (Y_WIDTH > 1) begin:V + assign Y[Y_WIDTH-1:1] = 0; + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$logic_or (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 1; +parameter B_WIDTH = 1; +parameter Y_WIDTH = 1; + +input [A_WIDTH-1:0] A; +input [B_WIDTH-1:0] B; +output [Y_WIDTH-1:0] Y; + +wire A_buf, B_buf; + +\$reduce_bool #( + .A_SIGNED(A_SIGNED), + .A_WIDTH(A_WIDTH) +) A_logic ( + .A(A), + .Y(A_buf) +); + +\$reduce_bool #( + .A_SIGNED(B_SIGNED), + .A_WIDTH(B_WIDTH) +) B_logic ( + .A(B), + .Y(B_buf) +); + + \$_OR_ gate ( + .A(A_buf), + .B(B_buf), + .Y(Y[0]) +); + +generate + if (Y_WIDTH > 1) begin:V + assign Y[Y_WIDTH-1:1] = 0; + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$mux (A, B, S, Y); + +parameter WIDTH = 1; + +input [WIDTH-1:0] A, B; +input S; +output [WIDTH-1:0] Y; + +genvar i; +generate + for (i = 0; i < WIDTH; i = i + 1) begin:V + \$_MUX_ gate ( + .A(A[i]), + .B(B[i]), + .S(S), + .Y(Y[i]) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$pmux (A, B, S, Y); + +parameter WIDTH = 1; +parameter S_WIDTH = 1; + +input [WIDTH-1:0] A; +input [WIDTH*S_WIDTH-1:0] B; +input [S_WIDTH-1:0] S; +output [WIDTH-1:0] Y; + +wire [WIDTH-1:0] Y_B; + +genvar i, j; +generate + wire [WIDTH*S_WIDTH-1:0] B_AND_S; + for (i = 0; i < S_WIDTH; i = i + 1) begin:B_AND + assign B_AND_S[WIDTH*(i+1)-1:WIDTH*i] = B[WIDTH*(i+1)-1:WIDTH*i] & {WIDTH{S[i]}}; + end:B_AND + for (i = 0; i < WIDTH; i = i + 1) begin:B_OR + wire [S_WIDTH-1:0] B_AND_BITS; + for (j = 0; j < S_WIDTH; j = j + 1) begin:B_AND_BITS_COLLECT + assign B_AND_BITS[j] = B_AND_S[WIDTH*j+i]; + end:B_AND_BITS_COLLECT + assign Y_B[i] = |B_AND_BITS; + end:B_OR +endgenerate + +assign Y = |S ? Y_B : A; + +endmodule + +// -------------------------------------------------------- + +module \$safe_pmux (A, B, S, Y); + +parameter WIDTH = 1; +parameter S_WIDTH = 1; + +input [WIDTH-1:0] A; +input [WIDTH*S_WIDTH-1:0] B; +input [S_WIDTH-1:0] S; +output [WIDTH-1:0] Y; + +wire [S_WIDTH-1:0] status_found_first; +wire [S_WIDTH-1:0] status_found_second; + +genvar i; +generate + for (i = 0; i < S_WIDTH; i = i + 1) begin:GEN1 + wire pre_first; + if (i > 0) begin:GEN2 + assign pre_first = status_found_first[i-1]; + end:GEN2 else begin:GEN3 + assign pre_first = 0; + end:GEN3 + assign status_found_first[i] = pre_first | S[i]; + assign status_found_second[i] = pre_first & S[i]; + end:GEN1 +endgenerate + +\$pmux #( + .WIDTH(WIDTH), + .S_WIDTH(S_WIDTH) +) pmux_cell ( + .A(A), + .B(B), + .S(S & {S_WIDTH{~|status_found_second}}), + .Y(Y) +); + +endmodule + +// -------------------------------------------------------- + +module \$dff (CLK, D, Q); + +parameter WIDTH = 1; +parameter CLK_POLARITY = 1'b1; + +input CLK; +input [WIDTH-1:0] D; +output [WIDTH-1:0] Q; + +genvar i; +generate + if (CLK_POLARITY == 0) + for (i = 0; i < WIDTH; i = i + 1) begin:V + \$_DFF_N_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK) + ); + end + if (CLK_POLARITY != 0) + for (i = 0; i < WIDTH; i = i + 1) begin:V + \$_DFF_P_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK) + ); + end +endgenerate + +endmodule + +// -------------------------------------------------------- + +module \$adff (CLK, ARST, D, Q); + +parameter WIDTH = 1; +parameter CLK_POLARITY = 1'b1; +parameter ARST_POLARITY = 1'b1; +parameter ARST_VALUE = 0; + +input CLK, ARST; +input [WIDTH-1:0] D; +output [WIDTH-1:0] Q; + +genvar i; +generate + for (i = 0; i < WIDTH; i = i + 1) begin:V + if (CLK_POLARITY == 0 && ARST_POLARITY == 0 && ARST_VALUE[i] == 0) begin:NN0 + \$_DFF_NN0_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY == 0 && ARST_POLARITY == 0 && ARST_VALUE[i] != 0) begin:NN1 + \$_DFF_NN1_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY == 0 && ARST_POLARITY != 0 && ARST_VALUE[i] == 0) begin:NP0 + \$_DFF_NP0_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY == 0 && ARST_POLARITY != 0 && ARST_VALUE[i] != 0) begin:NP1 + \$_DFF_NP1_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY != 0 && ARST_POLARITY == 0 && ARST_VALUE[i] == 0) begin:PN0 + \$_DFF_PN0_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY != 0 && ARST_POLARITY == 0 && ARST_VALUE[i] != 0) begin:PN1 + \$_DFF_PN1_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY != 0 && ARST_POLARITY != 0 && ARST_VALUE[i] == 0) begin:PP0 + \$_DFF_PP0_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + if (CLK_POLARITY != 0 && ARST_POLARITY != 0 && ARST_VALUE[i] != 0) begin:PP1 + \$_DFF_PP1_ ff ( + .D(D[i]), + .Q(Q[i]), + .C(CLK), + .R(ARST) + ); + end + end +endgenerate + +endmodule + +// -------------------------------------------------------- + diff --git a/techlibs/stdcells_sim.v b/techlibs/stdcells_sim.v new file mode 100644 index 000000000..6e5d2719a --- /dev/null +++ b/techlibs/stdcells_sim.v @@ -0,0 +1,166 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The internal logic cell simulation library. + * + * This verilog library contains simple simulation models for the internal + * logic cells ($_INV_ , $_AND_ , ...) that are generated by the default technology + * mapper (see "stdcells.v" in this directory) and expected by the "abc" pass. + * + */ + +module \$_INV_ (A, Y); +input A; +output Y; +assign Y = ~A; +endmodule + +module \$_AND_ (A, B, Y); +input A, B; +output Y; +assign Y = A & B; +endmodule + +module \$_OR_ (A, B, Y); +input A, B; +output Y; +assign Y = A | B; +endmodule + +module \$_XOR_ (A, B, Y); +input A, B; +output Y; +assign Y = A ^ B; +endmodule + +module \$_MUX_ (A, B, S, Y); +input A, B, S; +output reg Y; +always @* begin + if (S) + Y = B; + else + Y = A; +end +endmodule + +module \$_DFF_N_ (D, Q, C); +input D, C; +output reg Q; +always @(negedge C) begin + Q <= D; +end +endmodule + +module \$_DFF_P_ (D, Q, C); +input D, C; +output reg Q; +always @(posedge C) begin + Q <= D; +end +endmodule + +module \$_DFF_NN0_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(negedge C or negedge R) begin + if (R == 0) + Q <= 0; + else + Q <= D; +end +endmodule + +module \$_DFF_NN1_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(negedge C or negedge R) begin + if (R == 0) + Q <= 1; + else + Q <= D; +end +endmodule + +module \$_DFF_NP0_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(negedge C or posedge R) begin + if (R == 1) + Q <= 0; + else + Q <= D; +end +endmodule + +module \$_DFF_NP1_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(negedge C or posedge R) begin + if (R == 1) + Q <= 1; + else + Q <= D; +end +endmodule + +module \$_DFF_PN0_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(posedge C or negedge R) begin + if (R == 0) + Q <= 0; + else + Q <= D; +end +endmodule + +module \$_DFF_PN1_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(posedge C or negedge R) begin + if (R == 0) + Q <= 1; + else + Q <= D; +end +endmodule + +module \$_DFF_PP0_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(posedge C or posedge R) begin + if (R == 1) + Q <= 0; + else + Q <= D; +end +endmodule + +module \$_DFF_PP1_ (D, Q, C, R); +input D, C, R; +output reg Q; +always @(posedge C or posedge R) begin + if (R == 1) + Q <= 1; + else + Q <= D; +end +endmodule + diff --git a/tests/asicworld/README b/tests/asicworld/README new file mode 100644 index 000000000..0e96edb7b --- /dev/null +++ b/tests/asicworld/README @@ -0,0 +1 @@ +Borrowed verilog examples from http://www.asic-world.com/. diff --git a/tests/asicworld/code_hdl_models_GrayCounter.v b/tests/asicworld/code_hdl_models_GrayCounter.v new file mode 100644 index 000000000..23f0da04b --- /dev/null +++ b/tests/asicworld/code_hdl_models_GrayCounter.v @@ -0,0 +1,33 @@ +//========================================== +// Function : Code Gray counter. +// Coder : Alex Claros F. +// Date : 15/May/2005. +//======================================= + +module GrayCounter + #(parameter COUNTER_WIDTH = 4) + + (output reg [COUNTER_WIDTH-1:0] GrayCount_out, //'Gray' code count output. + + input wire Enable_in, //Count enable. + input wire Clear_in, //Count reset. + + input wire Clk); + + /////////Internal connections & variables/////// + reg [COUNTER_WIDTH-1:0] BinaryCount; + + /////////Code/////////////////////// + + always @ (posedge Clk) + if (Clear_in) begin + BinaryCount <= {COUNTER_WIDTH{1'b 0}} + 1; //Gray count begins @ '1' with + GrayCount_out <= {COUNTER_WIDTH{1'b 0}}; // first 'Enable_in'. + end + else if (Enable_in) begin + BinaryCount <= BinaryCount + 1; + GrayCount_out <= {BinaryCount[COUNTER_WIDTH-1], + BinaryCount[COUNTER_WIDTH-2:0] ^ BinaryCount[COUNTER_WIDTH-1:1]}; + end + +endmodule diff --git a/tests/asicworld/code_hdl_models_arbiter.v b/tests/asicworld/code_hdl_models_arbiter.v new file mode 100644 index 000000000..978e1987b --- /dev/null +++ b/tests/asicworld/code_hdl_models_arbiter.v @@ -0,0 +1,123 @@ +//---------------------------------------------------- +// A four level, round-robin arbiter. This was +// orginally coded by WD Peterson in VHDL. +//---------------------------------------------------- +module arbiter ( + clk, + rst, + req3, + req2, + req1, + req0, + gnt3, + gnt2, + gnt1, + gnt0 +); +// --------------Port Declaration----------------------- +input clk; +input rst; +input req3; +input req2; +input req1; +input req0; +output gnt3; +output gnt2; +output gnt1; +output gnt0; + +//--------------Internal Registers---------------------- +wire [1:0] gnt ; +wire comreq ; +wire beg ; +wire [1:0] lgnt ; +wire lcomreq ; +reg lgnt0 ; +reg lgnt1 ; +reg lgnt2 ; +reg lgnt3 ; +reg lasmask ; +reg lmask0 ; +reg lmask1 ; +reg ledge ; + +//--------------Code Starts Here----------------------- +always @ (posedge clk) +if (rst) begin + lgnt0 <= 0; + lgnt1 <= 0; + lgnt2 <= 0; + lgnt3 <= 0; +end else begin + lgnt0 <=(~lcomreq & ~lmask1 & ~lmask0 & ~req3 & ~req2 & ~req1 & req0) + | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req0) + | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req0) + | (~lcomreq & lmask1 & lmask0 & req0 ) + | ( lcomreq & lgnt0 ); + lgnt1 <=(~lcomreq & ~lmask1 & ~lmask0 & req1) + | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req1 & ~req0) + | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req1 & ~req0) + | (~lcomreq & lmask1 & lmask0 & req1 & ~req0) + | ( lcomreq & lgnt1); + lgnt2 <=(~lcomreq & ~lmask1 & ~lmask0 & req2 & ~req1) + | (~lcomreq & ~lmask1 & lmask0 & req2) + | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req2 & ~req1 & ~req0) + | (~lcomreq & lmask1 & lmask0 & req2 & ~req1 & ~req0) + | ( lcomreq & lgnt2); + lgnt3 <=(~lcomreq & ~lmask1 & ~lmask0 & req3 & ~req2 & ~req1) + | (~lcomreq & ~lmask1 & lmask0 & req3 & ~req2) + | (~lcomreq & lmask1 & ~lmask0 & req3) + | (~lcomreq & lmask1 & lmask0 & req3 & ~req2 & ~req1 & ~req0) + | ( lcomreq & lgnt3); +end + +//---------------------------------------------------- +// lasmask state machine. +//---------------------------------------------------- +assign beg = (req3 | req2 | req1 | req0) & ~lcomreq; +always @ (posedge clk) +begin + lasmask <= (beg & ~ledge & ~lasmask); + ledge <= (beg & ~ledge & lasmask) + | (beg & ledge & ~lasmask); +end + +//---------------------------------------------------- +// comreq logic. +//---------------------------------------------------- +assign lcomreq = ( req3 & lgnt3 ) + | ( req2 & lgnt2 ) + | ( req1 & lgnt1 ) + | ( req0 & lgnt0 ); + +//---------------------------------------------------- +// Encoder logic. +//---------------------------------------------------- +assign lgnt = {(lgnt3 | lgnt2),(lgnt3 | lgnt1)}; + +//---------------------------------------------------- +// lmask register. +//---------------------------------------------------- +always @ (posedge clk ) +if( rst ) begin + lmask1 <= 0; + lmask0 <= 0; +end else if(lasmask) begin + lmask1 <= lgnt[1]; + lmask0 <= lgnt[0]; +end else begin + lmask1 <= lmask1; + lmask0 <= lmask0; +end + +assign comreq = lcomreq; +assign gnt = lgnt; +//---------------------------------------------------- +// Drive the outputs +//---------------------------------------------------- +assign gnt3 = lgnt3; +assign gnt2 = lgnt2; +assign gnt1 = lgnt1; +assign gnt0 = lgnt0; + +endmodule diff --git a/tests/asicworld/code_hdl_models_arbiter_tb.v b/tests/asicworld/code_hdl_models_arbiter_tb.v new file mode 100644 index 000000000..5e7bf46b9 --- /dev/null +++ b/tests/asicworld/code_hdl_models_arbiter_tb.v @@ -0,0 +1,62 @@ +module testbench (); + +reg clk; +reg rst; +reg req3; +reg req2; +reg req1; +reg req0; +wire gnt3; +wire gnt2; +wire gnt1; +wire gnt0; + +// Clock generator +always #1 clk = ~clk; + +initial begin + $dumpfile ("arbiter.vcd"); + $dumpvars(); + clk = 0; + rst = 1; + req0 = 0; + req1 = 0; + req2 = 0; + req3 = 0; + #10 rst = 0; + repeat (1) @ (posedge clk); + req0 <= 1; + repeat (1) @ (posedge clk); + req0 <= 0; + repeat (1) @ (posedge clk); + req0 <= 1; + req1 <= 1; + repeat (1) @ (posedge clk); + req2 <= 1; + req1 <= 0; + repeat (1) @ (posedge clk); + req3 <= 1; + req2 <= 0; + repeat (1) @ (posedge clk); + req3 <= 0; + repeat (1) @ (posedge clk); + req0 <= 0; + repeat (1) @ (posedge clk); + #10 $finish; +end + +// Connect the DUT +arbiter U ( + clk, + rst, + req3, + req2, + req1, + req0, + gnt3, + gnt2, + gnt1, + gnt0 +); + +endmodule diff --git a/tests/asicworld/code_hdl_models_cam.v b/tests/asicworld/code_hdl_models_cam.v new file mode 100644 index 000000000..0cebc07cc --- /dev/null +++ b/tests/asicworld/code_hdl_models_cam.v @@ -0,0 +1,60 @@ +//----------------------------------------------------- +// Design Name : cam +// File Name : cam.v +// Function : CAM +// Coder : Deepak Kumar Tala +//----------------------------------------------------- +module cam ( +clk , // Cam clock +cam_enable , // Cam enable +cam_data_in , // Cam data to match +cam_hit_out , // Cam match has happened +cam_addr_out // Cam output address +); + +parameter ADDR_WIDTH = 8; +parameter DEPTH = 1 << ADDR_WIDTH; +//------------Input Ports-------------- +input clk; +input cam_enable; +input [DEPTH-1:0] cam_data_in; +//----------Output Ports-------------- +output cam_hit_out; +output [ADDR_WIDTH-1:0] cam_addr_out; +//------------Internal Variables-------- +reg [ADDR_WIDTH-1:0] cam_addr_out; +reg cam_hit_out; +reg [ADDR_WIDTH-1:0] cam_addr_combo; +reg cam_hit_combo; +reg found_match; +integer i; +//-------------Code Starts Here------- +always @(cam_data_in) begin + cam_addr_combo = {ADDR_WIDTH{1'b0}}; + found_match = 1'b0; + cam_hit_combo = 1'b0; + for (i=0; i 0 && rx_cnt < 9) begin + rx_reg[rx_cnt - 1] <= rx_d2; + end + if (rx_cnt == 9) begin + rx_busy <= 0; + // Check if End of frame received correctly + if (rx_d2 == 0) begin + rx_frame_err <= 1; + end else begin + rx_empty <= 0; + rx_frame_err <= 0; + // Check if last rx data was not unloaded, + rx_over_run <= (rx_empty) ? 0 : 1; + end + end + end + end + end + end + if (!rx_enable) begin + rx_busy <= 0; + end +end + +// UART TX Logic +always @ (posedge txclk or posedge reset) +if (reset) begin + tx_reg <= 0; + tx_empty <= 1; + tx_over_run <= 0; + tx_out <= 1; + tx_cnt <= 0; +end else begin + if (ld_tx_data) begin + if (!tx_empty) begin + tx_over_run <= 0; + end else begin + tx_reg <= tx_data; + tx_empty <= 0; + end + end + if (tx_enable && !tx_empty) begin + tx_cnt <= tx_cnt + 1; + if (tx_cnt == 0) begin + tx_out <= 0; + end + if (tx_cnt > 0 && tx_cnt < 9) begin + tx_out <= tx_reg[tx_cnt -1]; + end + if (tx_cnt == 9) begin + tx_out <= 1; + tx_cnt <= 0; + tx_empty <= 1; + end + end + if (!tx_enable) begin + tx_cnt <= 0; + end +end + +endmodule diff --git a/tests/asicworld/code_hdl_models_up_counter.v b/tests/asicworld/code_hdl_models_up_counter.v new file mode 100644 index 000000000..ffe670994 --- /dev/null +++ b/tests/asicworld/code_hdl_models_up_counter.v @@ -0,0 +1,29 @@ +//----------------------------------------------------- +// Design Name : up_counter +// File Name : up_counter.v +// Function : Up counter +// Coder  : Deepak +//----------------------------------------------------- +module up_counter ( +out , // Output of the counter +enable , // enable for counter +clk , // clock Input +reset // reset Input +); +//----------Output Ports-------------- + output [7:0] out; +//------------Input Ports-------------- + input enable, clk, reset; +//------------Internal Variables-------- + reg [7:0] out; +//-------------Code Starts Here------- +always @(posedge clk) +if (reset) begin + out <= 8'b0 ; +end else if (enable) begin + out <= out + 1; +end + + +endmodule + diff --git a/tests/asicworld/code_hdl_models_up_counter_load.v b/tests/asicworld/code_hdl_models_up_counter_load.v new file mode 100644 index 000000000..92ad895aa --- /dev/null +++ b/tests/asicworld/code_hdl_models_up_counter_load.v @@ -0,0 +1,32 @@ +//----------------------------------------------------- +// Design Name : up_counter_load +// File Name : up_counter_load.v +// Function : Up counter with load +// Coder : Deepak Kumar Tala +//----------------------------------------------------- +module up_counter_load ( +out , // Output of the counter +data , // Parallel load for the counter +load , // Parallel load enable +enable , // Enable counting +clk , // clock input +reset // reset input +); +//----------Output Ports-------------- +output [7:0] out; +//------------Input Ports-------------- +input [7:0] data; +input load, enable, clk, reset; +//------------Internal Variables-------- +reg [7:0] out; +//-------------Code Starts Here------- +always @(posedge clk) +if (reset) begin + out <= 8'b0 ; +end else if (load) begin + out <= data; +end else if (enable) begin + out <= out + 1; +end + +endmodule diff --git a/tests/asicworld/code_hdl_models_up_down_counter.v b/tests/asicworld/code_hdl_models_up_down_counter.v new file mode 100644 index 000000000..fff2982af --- /dev/null +++ b/tests/asicworld/code_hdl_models_up_down_counter.v @@ -0,0 +1,29 @@ +//----------------------------------------------------- +// Design Name : up_down_counter +// File Name : up_down_counter.v +// Function : Up down counter +// Coder : Deepak Kumar Tala +//----------------------------------------------------- +module up_down_counter ( +out , // Output of the counter +up_down , // up_down control for counter +clk , // clock input +reset // reset input +); +//----------Output Ports-------------- +output [7:0] out; +//------------Input Ports-------------- +input up_down, clk, reset; +//------------Internal Variables-------- +reg [7:0] out; +//-------------Code Starts Here------- +always @(posedge clk) +if (reset) begin // active high reset + out <= 8'b0 ; +end else if (up_down) begin + out <= out + 1; +end else begin + out <= out - 1; +end + +endmodule diff --git a/tests/asicworld/code_specman_switch_fabric.v b/tests/asicworld/code_specman_switch_fabric.v new file mode 100644 index 000000000..1ac7ee701 --- /dev/null +++ b/tests/asicworld/code_specman_switch_fabric.v @@ -0,0 +1,82 @@ +module switch_fabric( + clk, reset, data_in0, data_in1, data_in2, + data_in3, data_in4, data_in5, data_in_valid0, + data_in_valid1, data_in_valid2, data_in_valid3, + data_in_valid4, data_in_valid5, data_out0, + data_out1, data_out2, data_out3, data_out4, + data_out5, data_out_ack0, data_out_ack1, + data_out_ack2, data_out_ack3, data_out_ack4, + data_out_ack5 +); + +input clk, reset; +input [7:0] data_in0, data_in1, data_in2, data_in3; +input [7:0] data_in4, data_in5; +input data_in_valid0, data_in_valid1, data_in_valid2; +input [7:0] data_in_valid3, data_in_valid4, data_in_valid5; +output [7:0] data_out0, data_out1, data_out2, data_out3; +output [7:0] data_out4, data_out5; +output data_out_ack0, data_out_ack1, data_out_ack2; +output [7:0] data_out_ack3, data_out_ack4, data_out_ack5; + +(* gentb_clock *) +wire clk; + +switch port_0 ( .clk(clk), .reset(reset), .data_in(data_in0), + .data_in_valid(data_in_valid0), .data_out(data_out0), + .data_out_ack(data_out_ack0)); + +switch port_1 ( .clk(clk), .reset(reset), .data_in(data_in1), + .data_in_valid(data_in_valid1), .data_out(data_out1), + .data_out_ack(data_out_ack1)); + +switch port_2 ( .clk(clk), .reset(reset), .data_in(data_in2), + .data_in_valid(data_in_valid2), .data_out(data_out2), . + data_out_ack(data_out_ack2)); + +switch port_3 ( .clk(clk), .reset(reset), .data_in(data_in3), + .data_in_valid(data_in_valid3), .data_out(data_out3), + .data_out_ack(data_out_ack3)); + +switch port_4 ( .clk(clk), .reset(reset), .data_in(data_in4), + .data_in_valid(data_in_valid4), .data_out(data_out4), + .data_out_ack(data_out_ack4)); + +switch port_5 ( .clk(clk), .reset(reset), .data_in(data_in5), + .data_in_valid(data_in_valid5), .data_out(data_out5), + .data_out_ack(data_out_ack5)); + +endmodule + +module switch ( + clk, + reset, + data_in, + data_in_valid, + data_out, + data_out_ack +); + +input clk; +input reset; +input [7:0] data_in; +input data_in_valid; +output [7:0] data_out; +output data_out_ack; + +reg [7:0] data_out; +reg data_out_ack; + +always @ (posedge clk) +if (reset) begin + data_out <= 0; + data_out_ack <= 0; +end else if (data_in_valid) begin + data_out <= data_in; + data_out_ack <= 1; +end else begin + data_out <= 0; + data_out_ack <= 0; +end + +endmodule diff --git a/tests/asicworld/code_tidbits_asyn_reset.v b/tests/asicworld/code_tidbits_asyn_reset.v new file mode 100644 index 000000000..58e47c567 --- /dev/null +++ b/tests/asicworld/code_tidbits_asyn_reset.v @@ -0,0 +1,18 @@ +module asyn_reset(clk,reset,a,c); + input clk; + input reset; + input a; + output c; + + wire clk; + wire reset; + wire a; + reg c; + +always @ (posedge clk or posedge reset) + if ( reset == 1'b1) begin + c <= 0; + end else begin + c <= a; + end +endmodule diff --git a/tests/asicworld/code_tidbits_blocking.v b/tests/asicworld/code_tidbits_blocking.v new file mode 100644 index 000000000..e13b72cc7 --- /dev/null +++ b/tests/asicworld/code_tidbits_blocking.v @@ -0,0 +1,17 @@ +module blocking (clk,a,c); +input clk; +input a; +output c; + +wire clk; +wire a; +reg c; +reg b; + +always @ (posedge clk ) +begin + b = a; + c = b; +end + +endmodule diff --git a/tests/asicworld/code_tidbits_fsm_using_always.v b/tests/asicworld/code_tidbits_fsm_using_always.v new file mode 100644 index 000000000..8a8775b95 --- /dev/null +++ b/tests/asicworld/code_tidbits_fsm_using_always.v @@ -0,0 +1,91 @@ +//----------------------------------------------------- +// This is FSM demo program using always block +// Design Name : fsm_using_always +// File Name : fsm_using_always.v +//----------------------------------------------------- +module fsm_using_always ( +clock , // clock +reset , // Active high, syn reset +req_0 , // Request 0 +req_1 , // Request 1 +gnt_0 , // Grant 0 +gnt_1 +); +//-------------Input Ports----------------------------- +input clock,reset,req_0,req_1; + //-------------Output Ports---------------------------- +output gnt_0,gnt_1; +//-------------Input ports Data Type------------------- +wire clock,reset,req_0,req_1; +//-------------Output Ports Data Type------------------ +reg gnt_0,gnt_1; +//-------------Internal Constants-------------------------- +parameter SIZE = 3 ; +parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100 ; +//-------------Internal Variables--------------------------- +reg [SIZE-1:0] state ;// Seq part of the FSM +reg [SIZE-1:0] next_state ;// combo part of FSM +//----------Code startes Here------------------------ +always @ (state or req_0 or req_1) +begin : FSM_COMBO + next_state = 3'b000; + case(state) + IDLE : if (req_0 == 1'b1) begin + next_state = GNT0; + end else if (req_1 == 1'b1) begin + next_state= GNT1; + end else begin + next_state = IDLE; + end + GNT0 : if (req_0 == 1'b1) begin + next_state = GNT0; + end else begin + next_state = IDLE; + end + GNT1 : if (req_1 == 1'b1) begin + next_state = GNT1; + end else begin + next_state = IDLE; + end + default : next_state = IDLE; + endcase +end +//----------Seq Logic----------------------------- +always @ (posedge clock) +begin : FSM_SEQ + if (reset == 1'b1) begin + state <= #1 IDLE; + end else begin + state <= #1 next_state; + end +end +//----------Output Logic----------------------------- +always @ (posedge clock) +begin : OUTPUT_LOGIC +if (reset == 1'b1) begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b0; +end +else begin + case(state) + IDLE : begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b0; + end + GNT0 : begin + gnt_0 <= #1 1'b1; + gnt_1 <= #1 1'b0; + end + GNT1 : begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b1; + end + default : begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b0; + end + endcase +end +end // End Of Block OUTPUT_LOGIC + +endmodule // End of Module arbiter diff --git a/tests/asicworld/code_tidbits_fsm_using_function.v b/tests/asicworld/code_tidbits_fsm_using_function.v new file mode 100644 index 000000000..404498a01 --- /dev/null +++ b/tests/asicworld/code_tidbits_fsm_using_function.v @@ -0,0 +1,94 @@ +//----------------------------------------------------- +// This is FSM demo program using function +// Design Name : fsm_using_function +// File Name : fsm_using_function.v +//----------------------------------------------------- +module fsm_using_function ( +clock , // clock +reset , // Active high, syn reset +req_0 , // Request 0 +req_1 , // Request 1 +gnt_0 , // Grant 0 +gnt_1 +); +//-------------Input Ports----------------------------- +input clock,reset,req_0,req_1; + //-------------Output Ports---------------------------- +output gnt_0,gnt_1; +//-------------Input ports Data Type------------------- +wire clock,reset,req_0,req_1; +//-------------Output Ports Data Type------------------ +reg gnt_0,gnt_1; +//-------------Internal Constants-------------------------- +parameter SIZE = 3 ; +parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100 ; +//-------------Internal Variables--------------------------- +reg [SIZE-1:0] state ;// Seq part of the FSM +wire [SIZE-1:0] next_state ;// combo part of FSM +//----------Code startes Here------------------------ +assign next_state = fsm_function(state, req_0, req_1); +//----------Function for Combo Logic----------------- +function [SIZE-1:0] fsm_function; + input [SIZE-1:0] state ; + input req_0 ; + input req_1 ; + case(state) + IDLE : if (req_0 == 1'b1) begin + fsm_function = GNT0; + end else if (req_1 == 1'b1) begin + fsm_function= GNT1; + end else begin + fsm_function = IDLE; + end + GNT0 : if (req_0 == 1'b1) begin + fsm_function = GNT0; + end else begin + fsm_function = IDLE; + end + GNT1 : if (req_1 == 1'b1) begin + fsm_function = GNT1; + end else begin + fsm_function = IDLE; + end + default : fsm_function = IDLE; + endcase +endfunction +//----------Seq Logic----------------------------- +always @ (posedge clock) +begin : FSM_SEQ + if (reset == 1'b1) begin + state <= #1 IDLE; + end else begin + state <= #1 next_state; + end +end +//----------Output Logic----------------------------- +always @ (posedge clock) +begin : OUTPUT_LOGIC +if (reset == 1'b1) begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b0; +end +else begin + case(state) + IDLE : begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b0; + end + GNT0 : begin + gnt_0 <= #1 1'b1; + gnt_1 <= #1 1'b0; + end + GNT1 : begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b1; + end + default : begin + gnt_0 <= #1 1'b0; + gnt_1 <= #1 1'b0; + end + endcase +end +end // End Of Block OUTPUT_LOGIC + +endmodule // End of Module arbiter diff --git a/tests/asicworld/code_tidbits_fsm_using_single_always.v b/tests/asicworld/code_tidbits_fsm_using_single_always.v new file mode 100644 index 000000000..67cc08841 --- /dev/null +++ b/tests/asicworld/code_tidbits_fsm_using_single_always.v @@ -0,0 +1,63 @@ +//==================================================== +// This is FSM demo program using single always +// for both seq and combo logic +// Design Name : fsm_using_single_always +// File Name : fsm_using_single_always.v +//===================================================== +module fsm_using_single_always ( +clock , // clock +reset , // Active high, syn reset +req_0 , // Request 0 +req_1 , // Request 1 +gnt_0 , // Grant 0 +gnt_1 +); +//=============Input Ports============================= +input clock,reset,req_0,req_1; + //=============Output Ports=========================== +output gnt_0,gnt_1; +//=============Input ports Data Type=================== +wire clock,reset,req_0,req_1; +//=============Output Ports Data Type================== +reg gnt_0,gnt_1; +//=============Internal Constants====================== +parameter SIZE = 3 ; +parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100 ; +//=============Internal Variables====================== +reg [SIZE-1:0] state ;// Seq part of the FSM +reg [SIZE-1:0] next_state ;// combo part of FSM +//==========Code startes Here========================== +always @ (posedge clock) +begin : FSM +if (reset == 1'b1) begin + state <= #1 IDLE; + gnt_0 <= 0; + gnt_1 <= 0; +end else + case(state) + IDLE : if (req_0 == 1'b1) begin + state <= #1 GNT0; + gnt_0 <= 1; + end else if (req_1 == 1'b1) begin + gnt_1 <= 1; + state <= #1 GNT1; + end else begin + state <= #1 IDLE; + end + GNT0 : if (req_0 == 1'b1) begin + state <= #1 GNT0; + end else begin + gnt_0 <= 0; + state <= #1 IDLE; + end + GNT1 : if (req_1 == 1'b1) begin + state <= #1 GNT1; + end else begin + gnt_1 <= 0; + state <= #1 IDLE; + end + default : state <= #1 IDLE; +endcase +end + +endmodule // End of Module arbiter diff --git a/tests/asicworld/code_tidbits_nonblocking.v b/tests/asicworld/code_tidbits_nonblocking.v new file mode 100644 index 000000000..4a0d365e0 --- /dev/null +++ b/tests/asicworld/code_tidbits_nonblocking.v @@ -0,0 +1,17 @@ +module nonblocking (clk,a,c); +input clk; +input a; +output c; + +wire clk; +wire a; +reg c; +reg b; + +always @ (posedge clk ) +begin + b <= a; + c <= b; +end + +endmodule diff --git a/tests/asicworld/code_tidbits_reg_combo_example.v b/tests/asicworld/code_tidbits_reg_combo_example.v new file mode 100644 index 000000000..9689788c4 --- /dev/null +++ b/tests/asicworld/code_tidbits_reg_combo_example.v @@ -0,0 +1,13 @@ +module reg_combo_example( a, b, y); +input a, b; +output y; + +reg y; +wire a, b; + +always @ ( a or b) +begin + y = a & b; +end + +endmodule diff --git a/tests/asicworld/code_tidbits_reg_seq_example.v b/tests/asicworld/code_tidbits_reg_seq_example.v new file mode 100644 index 000000000..458c87927 --- /dev/null +++ b/tests/asicworld/code_tidbits_reg_seq_example.v @@ -0,0 +1,15 @@ +module reg_seq_example( clk, reset, d, q); +input clk, reset, d; +output q; + +reg q; +wire clk, reset, d; + +always @ (posedge clk or posedge reset) +if (reset) begin + q <= 1'b0; +end else begin + q <= d; +end + +endmodule diff --git a/tests/asicworld/code_tidbits_syn_reset.v b/tests/asicworld/code_tidbits_syn_reset.v new file mode 100644 index 000000000..994771b16 --- /dev/null +++ b/tests/asicworld/code_tidbits_syn_reset.v @@ -0,0 +1,19 @@ +module syn_reset (clk,reset,a,c); + input clk; + input reset; + input a; + output c; + + wire clk; + wire reset; + wire a; + reg c; + +always @ (posedge clk ) + if ( reset == 1'b1) begin + c <= 0; + end else begin + c <= a; + end + +endmodule diff --git a/tests/asicworld/code_tidbits_wire_example.v b/tests/asicworld/code_tidbits_wire_example.v new file mode 100644 index 000000000..577a535d1 --- /dev/null +++ b/tests/asicworld/code_tidbits_wire_example.v @@ -0,0 +1,9 @@ +module wire_example( a, b, y); + input a, b; + output y; + + wire a, b, y; + + assign y = a & b; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_addbit.v b/tests/asicworld/code_verilog_tutorial_addbit.v new file mode 100644 index 000000000..22063b052 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_addbit.v @@ -0,0 +1,24 @@ +module addbit ( +a , // first input +b , // Second input +ci , // Carry input +sum , // sum output +co // carry output +); +//Input declaration +input a; +input b; +input ci; +//Ouput declaration +output sum; +output co; +//Port Data types +wire a; +wire b; +wire ci; +wire sum; +wire co; +//Code starts here +assign {co,sum} = a + b + ci; + +endmodule // End of Module addbit diff --git a/tests/asicworld/code_verilog_tutorial_always_example.v b/tests/asicworld/code_verilog_tutorial_always_example.v new file mode 100644 index 000000000..8b0fc2067 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_always_example.v @@ -0,0 +1,11 @@ +module always_example(); +reg clk,reset,enable,q_in,data; + +always @ (posedge clk) +if (reset) begin + data <= 0; +end else if (enable) begin + data <= q_in; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_bus_con.v b/tests/asicworld/code_verilog_tutorial_bus_con.v new file mode 100644 index 000000000..b100c8136 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_bus_con.v @@ -0,0 +1,8 @@ +module bus_con (a,b, y); + input [3:0] a, b; + output [7:0] y; + wire [7:0] y; + + assign y = {a,b}; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_comment.v b/tests/asicworld/code_verilog_tutorial_comment.v new file mode 100644 index 000000000..1cc0eb424 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_comment.v @@ -0,0 +1,25 @@ +/* This is a + Multi line comment + example */ +module addbit ( +a, +b, +ci, +sum, +co); + +// Input Ports Single line comment +input a; +input b; +input ci; +// Output ports +output sum; +output co; +// Data Types +wire a; +wire b; +wire ci; +wire sum; +wire co; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_counter.v b/tests/asicworld/code_verilog_tutorial_counter.v new file mode 100644 index 000000000..534519745 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_counter.v @@ -0,0 +1,19 @@ +//----------------------------------------------------- +// Design Name : counter +// File Name : counter.v +// Function : 4 bit up counter +// Coder : Deepak +//----------------------------------------------------- +module counter (clk, reset, enable, count); +input clk, reset, enable; +output [3:0] count; +reg [3:0] count; + +always @ (posedge clk) +if (reset == 1'b1) begin + count <= 0; +end else if ( enable == 1'b1) begin + count <= count + 1; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_counter_tb.v b/tests/asicworld/code_verilog_tutorial_counter_tb.v new file mode 100644 index 000000000..104779381 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_counter_tb.v @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////// +// MODULE : counter_tb // +// TOP MODULE : -- // +// // +// PURPOSE : 4-bit up counter test bench // +// // +// DESIGNER : Deepak Kumar Tala // +// // +// Revision History // +// // +// DEVELOPMENT HISTORY : // +// Rev0.0 : Jan 03, 2003 // +// Initial Revision // +// // +/////////////////////////////////////////////////////////////////////////// +module testbench; + +reg clk, reset, enable; +wire [3:0] count; +reg dut_error; + +counter U0 ( +.clk (clk), +.reset (reset), +.enable (enable), +.count (count) +); + +event reset_enable; +event terminate_sim; + +initial +begin + $display ("###################################################"); + clk = 0; + reset = 0; + enable = 0; + dut_error = 0; +end + +always + #5 clk = !clk; + +initial +begin + $dumpfile ("counter.vcd"); + $dumpvars; +end + + +initial +@ (terminate_sim) begin + $display ("Terminating simulation"); + if (dut_error == 0) begin + $display ("Simulation Result : PASSED"); + end + else begin + $display ("Simulation Result : FAILED"); + end + $display ("###################################################"); + #1 $finish; +end + + + +event reset_done; + +initial +forever begin + @ (reset_enable); + @ (negedge clk) + $display ("Applying reset"); + reset = 1; + @ (negedge clk) + reset = 0; + $display ("Came out of Reset"); + -> reset_done; +end + +initial begin + #10 -> reset_enable; + @ (reset_done); + @ (negedge clk); + enable = 1; + repeat (5) + begin + @ (negedge clk); + end + enable = 0; + #5 -> terminate_sim; +end + + +reg [3:0] count_compare; + +always @ (posedge clk) +if (reset == 1'b1) + count_compare <= 0; +else if ( enable == 1'b1) + count_compare <= count_compare + 1; + + + +always @ (negedge clk) +if (count_compare != count) begin + $display ("DUT ERROR AT TIME%d",$time); + $display ("Expected value %d, Got Value %d", count_compare, count); + dut_error = 1; + #5 -> terminate_sim; +end + +endmodule + diff --git a/tests/asicworld/code_verilog_tutorial_d_ff.v b/tests/asicworld/code_verilog_tutorial_d_ff.v new file mode 100644 index 000000000..7a4083605 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_d_ff.v @@ -0,0 +1,14 @@ +// D flip-flop Code +module d_ff ( d, clk, q, q_bar); +input d ,clk; +output q, q_bar; +wire d ,clk; +reg q, q_bar; + +always @ (posedge clk) +begin + q <= d; + q_bar <= !d; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_decoder.v b/tests/asicworld/code_verilog_tutorial_decoder.v new file mode 100644 index 000000000..5efdbd7e7 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_decoder.v @@ -0,0 +1,14 @@ +module decoder (in,out); +input [2:0] in; +output [7:0] out; +wire [7:0] out; +assign out = (in == 3'b000 ) ? 8'b0000_0001 : +(in == 3'b001 ) ? 8'b0000_0010 : +(in == 3'b010 ) ? 8'b0000_0100 : +(in == 3'b011 ) ? 8'b0000_1000 : +(in == 3'b100 ) ? 8'b0001_0000 : +(in == 3'b101 ) ? 8'b0010_0000 : +(in == 3'b110 ) ? 8'b0100_0000 : +(in == 3'b111 ) ? 8'b1000_0000 : 8'h00; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_decoder_always.v b/tests/asicworld/code_verilog_tutorial_decoder_always.v new file mode 100644 index 000000000..4418ec700 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_decoder_always.v @@ -0,0 +1,20 @@ +module decoder_always (in,out); +input [2:0] in; +output [7:0] out; +reg [7:0] out; + +always @ (in) +begin + out = 0; + case (in) + 3'b001 : out = 8'b0000_0001; + 3'b010 : out = 8'b0000_0010; + 3'b011 : out = 8'b0000_0100; + 3'b100 : out = 8'b0000_1000; + 3'b101 : out = 8'b0001_0000; + 3'b110 : out = 8'b0100_0000; + 3'b111 : out = 8'b1000_0000; + endcase +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_escape_id.v b/tests/asicworld/code_verilog_tutorial_escape_id.v new file mode 100644 index 000000000..6c33da174 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_escape_id.v @@ -0,0 +1,14 @@ +// There must be white space after the +// string which uses escape character +module \1dff ( +q, // Q output +\q~ , // Q_out output +d, // D input +cl$k, // CLOCK input +\reset* // Reset input +); + +input d, cl$k, \reset* ; +output q, \q~ ; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_explicit.v b/tests/asicworld/code_verilog_tutorial_explicit.v new file mode 100644 index 000000000..88427ff08 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_explicit.v @@ -0,0 +1,35 @@ +module explicit(); +reg clk,d,rst,pre; +wire q; + +// Here q_bar is not connected +// We can connect ports in any order +dff u0 ( +.q (q), +.d (d), +.clk (clk), +.q_bar (), +.rst (rst), +.pre (pre) +); + +endmodule + +// D fli-flop +module dff (q, q_bar, clk, d, rst, pre); +input clk, d, rst, pre; +output q, q_bar; +reg q; + +assign q_bar = ~q; + +always @ (posedge clk) +if (rst == 1'b1) begin + q <= 0; +end else if (pre == 1'b1) begin + q <= 1; +end else begin + q <= d; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_first_counter.v b/tests/asicworld/code_verilog_tutorial_first_counter.v new file mode 100644 index 000000000..d35d4aacc --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_first_counter.v @@ -0,0 +1,47 @@ +//----------------------------------------------------- +// This is my second Verilog Design +// Design Name : first_counter +// File Name : first_counter.v +// Function : This is a 4 bit up-counter with +// Synchronous active high reset and +// with active high enable signal +//----------------------------------------------------- +module first_counter ( +clock , // Clock input of the design +reset , // active high, synchronous Reset input +enable , // Active high enable signal for counter +counter_out // 4 bit vector output of the counter +); // End of port list +//-------------Input Ports----------------------------- +input clock ; +input reset ; +input enable ; +//-------------Output Ports---------------------------- +output [3:0] counter_out ; +//-------------Input ports Data Type------------------- +// By rule all the input ports should be wires +wire clock ; +wire reset ; +wire enable ; +//-------------Output Ports Data Type------------------ +// Output port can be a storage element (reg) or a wire +reg [3:0] counter_out ; + +//------------Code Starts Here------------------------- +// Since this counter is a positive edge trigged one, +// We trigger the below block with respect to positive +// edge of the clock. +always @ (posedge clock) +begin : COUNTER // Block Name + // At every rising edge of clock we check if reset is active + // If active, we load the counter output with 4'b0000 + if (reset == 1'b1) begin + counter_out <= 4'b0000; + end + // If enable is active, then we increment the counter + else if (enable == 1'b1) begin + counter_out <= counter_out + 1; + end +end // End of Block COUNTER + +endmodule // End of Module counter diff --git a/tests/asicworld/code_verilog_tutorial_first_counter_tb.v b/tests/asicworld/code_verilog_tutorial_first_counter_tb.v new file mode 100644 index 000000000..f065732be --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_first_counter_tb.v @@ -0,0 +1,36 @@ +module testbench(); +// Declare inputs as regs and outputs as wires +reg clock, reset, enable; +wire [3:0] counter_out; + +// Initialize all variables +initial begin + $display ("time\t clk reset enable counter"); + $monitor ("%g\t %b %b %b %b", + $time, clock, reset, enable, counter_out); + clock = 1; // initial value of clock + reset = 0; // initial value of reset + enable = 0; // initial value of enable + #5 reset = 1; // Assert the reset + #10 reset = 0; // De-assert the reset + #10 enable = 1; // Assert enable + #100 enable = 0; // De-assert enable + #5 $finish; // Terminate simulation +end + +// Clock generator +initial begin + #1; + forever + #5 clock = ~clock; // Toggle clock every 5 ticks +end + +// Connect DUT to test bench +first_counter U_counter ( +clock, +reset, +enable, +counter_out +); + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_flip_flop.v b/tests/asicworld/code_verilog_tutorial_flip_flop.v new file mode 100644 index 000000000..ed2e88c2e --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_flip_flop.v @@ -0,0 +1,15 @@ +module flif_flop (clk,reset, q, d); +input clk, reset, d; +output q; +reg q; + +always @ (posedge clk ) +begin + if (reset == 1) begin + q <= 0; + end else begin + q <= d; + end +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_fsm_full.v b/tests/asicworld/code_verilog_tutorial_fsm_full.v new file mode 100644 index 000000000..fd2d559bb --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_fsm_full.v @@ -0,0 +1,114 @@ +module fsm_full( +clock , // Clock +reset , // Active high reset +req_0 , // Active high request from agent 0 +req_1 , // Active high request from agent 1 +req_2 , // Active high request from agent 2 +req_3 , // Active high request from agent 3 +gnt_0 , // Active high grant to agent 0 +gnt_1 , // Active high grant to agent 1 +gnt_2 , // Active high grant to agent 2 +gnt_3 // Active high grant to agent 3 +); +// Port declaration here +input clock ; // Clock +input reset ; // Active high reset +input req_0 ; // Active high request from agent 0 +input req_1 ; // Active high request from agent 1 +input req_2 ; // Active high request from agent 2 +input req_3 ; // Active high request from agent 3 +output gnt_0 ; // Active high grant to agent 0 +output gnt_1 ; // Active high grant to agent 1 +output gnt_2 ; // Active high grant to agent 2 +output gnt_3 ; // Active high grant to agent + +// Internal Variables +reg gnt_0 ; // Active high grant to agent 0 +reg gnt_1 ; // Active high grant to agent 1 +reg gnt_2 ; // Active high grant to agent 2 +reg gnt_3 ; // Active high grant to agent + +parameter [2:0] IDLE = 3'b000; +parameter [2:0] GNT0 = 3'b001; +parameter [2:0] GNT1 = 3'b010; +parameter [2:0] GNT2 = 3'b011; +parameter [2:0] GNT3 = 3'b100; + +reg [2:0] state, next_state; + +always @ (state or req_0 or req_1 or req_2 or req_3) +begin + next_state = 0; + case(state) + IDLE : if (req_0 == 1'b1) begin + next_state = GNT0; + end else if (req_1 == 1'b1) begin + next_state= GNT1; + end else if (req_2 == 1'b1) begin + next_state= GNT2; + end else if (req_3 == 1'b1) begin + next_state= GNT3; + end else begin + next_state = IDLE; + end + GNT0 : if (req_0 == 1'b0) begin + next_state = IDLE; + end else begin + next_state = GNT0; + end + GNT1 : if (req_1 == 1'b0) begin + next_state = IDLE; + end else begin + next_state = GNT1; + end + GNT2 : if (req_2 == 1'b0) begin + next_state = IDLE; + end else begin + next_state = GNT2; + end + GNT3 : if (req_3 == 1'b0) begin + next_state = IDLE; + end else begin + next_state = GNT3; + end + default : next_state = IDLE; + endcase +end + +always @ (posedge clock) +begin : OUTPUT_LOGIC + if (reset) begin + gnt_0 <= 1'b0; + gnt_1 <= 1'b0; + gnt_2 <= 1'b0; + gnt_3 <= 1'b0; + state <= IDLE; + end else begin + state <= next_state; + case(state) + IDLE : begin + gnt_0 <= 1'b0; + gnt_1 <= 1'b0; + gnt_2 <= 1'b0; + gnt_3 <= 1'b0; + end + GNT0 : begin + gnt_0 <= 1'b1; + end + GNT1 : begin + gnt_1 <= 1'b1; + end + GNT2 : begin + gnt_2 <= 1'b1; + end + GNT3 : begin + gnt_3 <= 1'b1; + end + default : begin + state <= IDLE; + end + endcase + end +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v b/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v new file mode 100644 index 000000000..0097b1c98 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v @@ -0,0 +1,48 @@ +module testbench(); +reg clock , reset ; +reg req_0 , req_1 , req_2 , req_3; +wire gnt_0 , gnt_1 , gnt_2 , gnt_3 ; + +initial begin + $display("Time\t R0 R1 R2 R3 G0 G1 G2 G3"); + $monitor("%g\t %b %b %b %b %b %b %b %b", + $time, req_0, req_1, req_2, req_3, gnt_0, gnt_1, gnt_2, gnt_3); + clock = 0; + reset = 0; + req_0 = 0; + req_1 = 0; + req_2 = 0; + req_3 = 0; + #10 reset = 1; + #10 reset = 0; + #10 req_0 = 1; + #20 req_0 = 0; + #10 req_1 = 1; + #20 req_1 = 0; + #10 req_2 = 1; + #20 req_2 = 0; + #10 req_3 = 1; + #20 req_3 = 0; + #10 $finish; +end + +initial begin + #1; + forever + #2 clock = ~clock; +end + +fsm_full U_fsm_full( +clock , // Clock +reset , // Active high reset +req_0 , // Active high request from agent 0 +req_1 , // Active high request from agent 1 +req_2 , // Active high request from agent 2 +req_3 , // Active high request from agent 3 +gnt_0 , // Active high grant to agent 0 +gnt_1 , // Active high grant to agent 1 +gnt_2 , // Active high grant to agent 2 +gnt_3 // Active high grant to agent 3 +); + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_good_code.v b/tests/asicworld/code_verilog_tutorial_good_code.v new file mode 100644 index 000000000..6ba776446 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_good_code.v @@ -0,0 +1,18 @@ + module addbit ( + a, + b, + ci, + sum, + co); + input a; + input b; + input ci; + output sum; + output co; + wire a; + wire b; + wire ci; + wire sum; + wire co; + + endmodule diff --git a/tests/asicworld/code_verilog_tutorial_if_else.v b/tests/asicworld/code_verilog_tutorial_if_else.v new file mode 100644 index 000000000..19b91d3f2 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_if_else.v @@ -0,0 +1,13 @@ +module if_else(); + +reg dff; +wire clk,din,reset; + +always @ (posedge clk) +if (reset) begin + dff <= 0; +end else begin + dff <= din; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_multiply.v b/tests/asicworld/code_verilog_tutorial_multiply.v new file mode 100644 index 000000000..1912e1e26 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_multiply.v @@ -0,0 +1,8 @@ +module muliply (a,product); + input [3:0] a; + output [4:0] product; + wire [4:0] product; + + assign product = a << 1; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_mux_21.v b/tests/asicworld/code_verilog_tutorial_mux_21.v new file mode 100644 index 000000000..a6a0d35eb --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_mux_21.v @@ -0,0 +1,9 @@ +module mux_21 (a,b,sel,y); + input a, b; + output y; + input sel; + wire y; + + assign y = (sel) ? b : a; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_n_out_primitive.v b/tests/asicworld/code_verilog_tutorial_n_out_primitive.v new file mode 100644 index 000000000..814385a45 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_n_out_primitive.v @@ -0,0 +1,13 @@ +module n_out_primitive(); + +wire out,out_0,out_1,out_2,out_3,out_a,out_b,out_c; +wire in; + +// one output Buffer gate +buf u_buf0 (out,in); +// four output Buffer gate +buf u_buf1 (out_0, out_1, out_2, out_3, in); +// three output Invertor gate +not u_not0 (out_a, out_b, out_c, in); + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_parallel_if.v b/tests/asicworld/code_verilog_tutorial_parallel_if.v new file mode 100644 index 000000000..1dbe737eb --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_parallel_if.v @@ -0,0 +1,21 @@ +module parallel_if(); + +reg [3:0] counter; +wire clk,reset,enable, up_en, down_en; + +always @ (posedge clk) +// If reset is asserted +if (reset == 1'b0) begin + counter <= 4'b0000; +end else begin + // If counter is enable and up count is mode + if (enable == 1'b1 && up_en == 1'b1) begin + counter <= counter + 1'b1; + end + // If counter is enable and down count is mode + if (enable == 1'b1 && down_en == 1'b1) begin + counter <= counter - 1'b1; + end +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_parity.v b/tests/asicworld/code_verilog_tutorial_parity.v new file mode 100644 index 000000000..764396c2f --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_parity.v @@ -0,0 +1,41 @@ +//----------------------------------------------------- +// This is simple parity Program +// Design Name : parity +// File Name : parity.v +// Function : This program shows how a verilog +// primitive/module port connection are done +// Coder : Deepak +//----------------------------------------------------- +module parity ( +a , // First input +b , // Second input +c , // Third Input +d , // Fourth Input +y // Parity output +); + +// Input Declaration +input a ; +input b ; +input c ; +input d ; +// Ouput Declaration +output y ; +// port data types +wire a ; +wire b ; +wire c ; +wire d ; +wire y ; +// Internal variables +wire out_0 ; +wire out_1 ; + +// Code starts Here +xor u0 (out_0,a,b); + +xor u1 (out_1,c,d); + +xor u2 (y,out_0,out_1); + +endmodule // End Of Module parity diff --git a/tests/asicworld/code_verilog_tutorial_simple_function.v b/tests/asicworld/code_verilog_tutorial_simple_function.v new file mode 100644 index 000000000..5818a1d4a --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_simple_function.v @@ -0,0 +1,10 @@ +module simple_function(); + +function myfunction; +input a, b, c, d; +begin + myfunction = ((a+b) + (c-d)); +end +endfunction + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_simple_if.v b/tests/asicworld/code_verilog_tutorial_simple_if.v new file mode 100644 index 000000000..a68cc4a87 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_simple_if.v @@ -0,0 +1,11 @@ +module simple_if(); + +reg latch; +wire enable,din; + +always @ (enable or din) +if (enable) begin + latch <= din; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_task_global.v b/tests/asicworld/code_verilog_tutorial_task_global.v new file mode 100644 index 000000000..3ae862797 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_task_global.v @@ -0,0 +1,12 @@ +module task_global(); + +reg [7:0] temp_out; +reg [7:0] temp_in; + +task convert; +begin + temp_out = (9/5) *( temp_in + 32); +end +endtask + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_tri_buf.v b/tests/asicworld/code_verilog_tutorial_tri_buf.v new file mode 100644 index 000000000..a55b29caa --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_tri_buf.v @@ -0,0 +1,9 @@ +module tri_buf (a,b,enable); + input a; + output b; + input enable; + wire b; + +assign b = (enable) ? a : 1'bz; + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_v2k_reg.v b/tests/asicworld/code_verilog_tutorial_v2k_reg.v new file mode 100644 index 000000000..537a9e854 --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_v2k_reg.v @@ -0,0 +1,24 @@ +module v2k_reg(); + +// v2k allows to init variables +reg a = 0; +// Here only last variable is set to 0, i.e d = 0 +// Rest b, c are set to x +reg b, c, d = 0; +// reg data type can be signed in v2k +// We can assign with signed constants +reg signed [7:0] data = 8'shF0; + +// Function can return signed values +// Its ports can contain signed ports +function signed [7:0] adder; + input a_in; + input b_in; + input c_in; + input signed [7:0] data_in; + begin + adder = a_in + b_in + c_in + data_in; + end +endfunction + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_which_clock.v b/tests/asicworld/code_verilog_tutorial_which_clock.v new file mode 100644 index 000000000..418a2cfac --- /dev/null +++ b/tests/asicworld/code_verilog_tutorial_which_clock.v @@ -0,0 +1,12 @@ +module which_clock (x,y,q,d); +input x,y,d; +output q; +reg q; + +always @ (posedge x or posedge y) + if (x) + q <= 1'b0; + else + q <= d; + +endmodule diff --git a/tests/asicworld/run-test.sh b/tests/asicworld/run-test.sh new file mode 100755 index 000000000..bf27d15f8 --- /dev/null +++ b/tests/asicworld/run-test.sh @@ -0,0 +1,3 @@ +#!/bin/bash +make -C ../.. || exit 1 +exec bash ../tools/autotest.sh *.v diff --git a/tests/hana/README b/tests/hana/README new file mode 100644 index 000000000..37049405d --- /dev/null +++ b/tests/hana/README @@ -0,0 +1,14 @@ + +This test cases are copied from the hana project: +https://sourceforge.net/projects/sim-sim/ + +** Copy tests from hana: ** +while read fn; do cp -v $fn ALL_TESTS/${fn//\//_}; done < <(find test -name '*.v' ! -name '*_gold.v') + +** Eliminate test's we can't parse atm: ** +rm -f test_synthesizability*.v +rm -f test_parse2synthtrans_latch_1_test.v +rm -f test_parse2synthtrans_always_1_test.v +rm -f test_parse2synthtrans_always_2_test.v +for x in test_*.v; do ../../yosys -b "" $x || rm $x; done + diff --git a/tests/hana/hana_vlib.v b/tests/hana/hana_vlib.v new file mode 100644 index 000000000..fc82f1389 --- /dev/null +++ b/tests/hana/hana_vlib.v @@ -0,0 +1,1139 @@ +/* +Copyright (C) 2009-2010 Parvez Ahmad +Written by Parvez Ahmad . + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . */ + + +module BUF (input in, output out); + +assign out = in; + +endmodule + +module TRIBUF(input in, enable, output out); + +assign out = enable ? in : 1'bz; + +endmodule + +module INV(input in, output out); + +assign out = ~in; + +endmodule + +module AND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); + +assign out = ∈ + +endmodule + +module AND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); + +assign out = ∈ + +endmodule + +module AND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); + +assign out = ∈ + +endmodule + +module OR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); + +assign out = |in; + +endmodule + +module OR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); + +assign out = |in; + +endmodule + +module OR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); + +assign out = |in; + +endmodule + + +module NAND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); + +assign out = ~∈ + +endmodule + +module NAND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); + +assign out = ~∈ + +endmodule + +module NAND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); + +assign out = ~∈ + +endmodule + +module NOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); + +assign out = ~|in; + +endmodule + +module NOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); + +assign out = ~|in; + +endmodule + +module NOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); + +assign out = ~|in; + +endmodule + + +module XOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); + +assign out = ^in; + +endmodule + +module XOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); + +assign out = ^in; + +endmodule + +module XOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); + +assign out = ^in; + +endmodule + + +module XNOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); + +assign out = ~^in; + +endmodule + +module XNOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); + +assign out = ~^in; + +endmodule + +module XNOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); + +assign out = ~^in; + +endmodule + +module DEC1 (input in, enable, output reg [1:0] out); + +always @(in or enable) + if(!enable) + out = 2'b00; + else begin + case (in) + 1'b0 : out = 2'b01; + 1'b1 : out = 2'b10; + endcase + end +endmodule + +module DEC2 (input [1:0] in, input enable, output reg [3:0] out); + +always @(in or enable) + if(!enable) + out = 4'b0000; + else begin + case (in) + 2'b00 : out = 4'b0001; + 2'b01 : out = 4'b0010; + 2'b10 : out = 4'b0100; + 2'b11 : out = 4'b1000; + endcase + end +endmodule + +module DEC3 (input [2:0] in, input enable, output reg [7:0] out); + +always @(in or enable) + if(!enable) + out = 8'b00000000; + else begin + case (in) + 3'b000 : out = 8'b00000001; + 3'b001 : out = 8'b00000010; + 3'b010 : out = 8'b00000100; + 3'b011 : out = 8'b00001000; + 3'b100 : out = 8'b00010000; + 3'b101 : out = 8'b00100000; + 3'b110 : out = 8'b01000000; + 3'b111 : out = 8'b10000000; + endcase + end +endmodule + +module DEC4 (input [3:0] in, input enable, output reg [15:0] out); + +always @(in or enable) + if(!enable) + out = 16'b0000000000000000; + else begin + case (in) + 4'b0000 : out = 16'b0000000000000001; + 4'b0001 : out = 16'b0000000000000010; + 4'b0010 : out = 16'b0000000000000100; + 4'b0011 : out = 16'b0000000000001000; + 4'b0100 : out = 16'b0000000000010000; + 4'b0101 : out = 16'b0000000000100000; + 4'b0110 : out = 16'b0000000001000000; + 4'b0111 : out = 16'b0000000010000000; + 4'b1000 : out = 16'b0000000100000000; + 4'b1001 : out = 16'b0000001000000000; + 4'b1010 : out = 16'b0000010000000000; + 4'b1011 : out = 16'b0000100000000000; + 4'b1100 : out = 16'b0001000000000000; + 4'b1101 : out = 16'b0010000000000000; + 4'b1110 : out = 16'b0100000000000000; + 4'b1111 : out = 16'b1000000000000000; + endcase + end +endmodule +module DEC5 (input [4:0] in, input enable, output reg [31:0] out); + +always @(in or enable) + if(!enable) + out = 32'b00000000000000000000000000000000; + else begin + case (in) + 5'b00000 : out = 32'b00000000000000000000000000000001; + 5'b00001 : out = 32'b00000000000000000000000000000010; + 5'b00010 : out = 32'b00000000000000000000000000000100; + 5'b00011 : out = 32'b00000000000000000000000000001000; + 5'b00100 : out = 32'b00000000000000000000000000010000; + 5'b00101 : out = 32'b00000000000000000000000000100000; + 5'b00110 : out = 32'b00000000000000000000000001000000; + 5'b00111 : out = 32'b00000000000000000000000010000000; + 5'b01000 : out = 32'b00000000000000000000000100000000; + 5'b01001 : out = 32'b00000000000000000000001000000000; + 5'b01010 : out = 32'b00000000000000000000010000000000; + 5'b01011 : out = 32'b00000000000000000000100000000000; + 5'b01100 : out = 32'b00000000000000000001000000000000; + 5'b01101 : out = 32'b00000000000000000010000000000000; + 5'b01110 : out = 32'b00000000000000000100000000000000; + 5'b01111 : out = 32'b00000000000000001000000000000000; + 5'b10000 : out = 32'b00000000000000010000000000000000; + 5'b10001 : out = 32'b00000000000000100000000000000000; + 5'b10010 : out = 32'b00000000000001000000000000000000; + 5'b10011 : out = 32'b00000000000010000000000000000000; + 5'b10100 : out = 32'b00000000000100000000000000000000; + 5'b10101 : out = 32'b00000000001000000000000000000000; + 5'b10110 : out = 32'b00000000010000000000000000000000; + 5'b10111 : out = 32'b00000000100000000000000000000000; + 5'b11000 : out = 32'b00000001000000000000000000000000; + 5'b11001 : out = 32'b00000010000000000000000000000000; + 5'b11010 : out = 32'b00000100000000000000000000000000; + 5'b11011 : out = 32'b00001000000000000000000000000000; + 5'b11100 : out = 32'b00010000000000000000000000000000; + 5'b11101 : out = 32'b00100000000000000000000000000000; + 5'b11110 : out = 32'b01000000000000000000000000000000; + 5'b11111 : out = 32'b10000000000000000000000000000000; + endcase + end +endmodule + +module DEC6 (input [5:0] in, input enable, output reg [63:0] out); + +always @(in or enable) + if(!enable) + out = 64'b0000000000000000000000000000000000000000000000000000000000000000; + else begin + case (in) + 6'b000000 : out = 64'b0000000000000000000000000000000000000000000000000000000000000001; + 6'b000001 : out = 64'b0000000000000000000000000000000000000000000000000000000000000010; + 6'b000010 : out = 64'b0000000000000000000000000000000000000000000000000000000000000100; + 6'b000011 : out = 64'b0000000000000000000000000000000000000000000000000000000000001000; + 6'b000100 : out = 64'b0000000000000000000000000000000000000000000000000000000000010000; + 6'b000101 : out = 64'b0000000000000000000000000000000000000000000000000000000000100000; + 6'b000110 : out = 64'b0000000000000000000000000000000000000000000000000000000001000000; + 6'b000111 : out = 64'b0000000000000000000000000000000000000000000000000000000010000000; + 6'b001000 : out = 64'b0000000000000000000000000000000000000000000000000000000100000000; + 6'b001001 : out = 64'b0000000000000000000000000000000000000000000000000000001000000000; + 6'b001010 : out = 64'b0000000000000000000000000000000000000000000000000000010000000000; + 6'b001011 : out = 64'b0000000000000000000000000000000000000000000000000000100000000000; + 6'b001100 : out = 64'b0000000000000000000000000000000000000000000000000001000000000000; + 6'b001101 : out = 64'b0000000000000000000000000000000000000000000000000010000000000000; + 6'b001110 : out = 64'b0000000000000000000000000000000000000000000000000100000000000000; + 6'b001111 : out = 64'b0000000000000000000000000000000000000000000000001000000000000000; + 6'b010000 : out = 64'b0000000000000000000000000000000000000000000000010000000000000000; + 6'b010001 : out = 64'b0000000000000000000000000000000000000000000000100000000000000000; + 6'b010010 : out = 64'b0000000000000000000000000000000000000000000001000000000000000000; + 6'b010011 : out = 64'b0000000000000000000000000000000000000000000010000000000000000000; + 6'b010100 : out = 64'b0000000000000000000000000000000000000000000100000000000000000000; + 6'b010101 : out = 64'b0000000000000000000000000000000000000000001000000000000000000000; + 6'b010110 : out = 64'b0000000000000000000000000000000000000000010000000000000000000000; + 6'b010111 : out = 64'b0000000000000000000000000000000000000000100000000000000000000000; + 6'b011000 : out = 64'b0000000000000000000000000000000000000001000000000000000000000000; + 6'b011001 : out = 64'b0000000000000000000000000000000000000010000000000000000000000000; + 6'b011010 : out = 64'b0000000000000000000000000000000000000100000000000000000000000000; + 6'b011011 : out = 64'b0000000000000000000000000000000000001000000000000000000000000000; + 6'b011100 : out = 64'b0000000000000000000000000000000000010000000000000000000000000000; + 6'b011101 : out = 64'b0000000000000000000000000000000000100000000000000000000000000000; + 6'b011110 : out = 64'b0000000000000000000000000000000001000000000000000000000000000000; + 6'b011111 : out = 64'b0000000000000000000000000000000010000000000000000000000000000000; + + 6'b100000 : out = 64'b0000000000000000000000000000000100000000000000000000000000000000; + 6'b100001 : out = 64'b0000000000000000000000000000001000000000000000000000000000000000; + 6'b100010 : out = 64'b0000000000000000000000000000010000000000000000000000000000000000; + 6'b100011 : out = 64'b0000000000000000000000000000100000000000000000000000000000000000; + 6'b100100 : out = 64'b0000000000000000000000000001000000000000000000000000000000000000; + 6'b100101 : out = 64'b0000000000000000000000000010000000000000000000000000000000000000; + 6'b100110 : out = 64'b0000000000000000000000000100000000000000000000000000000000000000; + 6'b100111 : out = 64'b0000000000000000000000001000000000000000000000000000000000000000; + 6'b101000 : out = 64'b0000000000000000000000010000000000000000000000000000000000000000; + 6'b101001 : out = 64'b0000000000000000000000100000000000000000000000000000000000000000; + 6'b101010 : out = 64'b0000000000000000000001000000000000000000000000000000000000000000; + 6'b101011 : out = 64'b0000000000000000000010000000000000000000000000000000000000000000; + 6'b101100 : out = 64'b0000000000000000000100000000000000000000000000000000000000000000; + 6'b101101 : out = 64'b0000000000000000001000000000000000000000000000000000000000000000; + 6'b101110 : out = 64'b0000000000000000010000000000000000000000000000000000000000000000; + 6'b101111 : out = 64'b0000000000000000100000000000000000000000000000000000000000000000; + 6'b110000 : out = 64'b0000000000000001000000000000000000000000000000000000000000000000; + 6'b110001 : out = 64'b0000000000000010000000000000000000000000000000000000000000000000; + 6'b110010 : out = 64'b0000000000000100000000000000000000000000000000000000000000000000; + 6'b110011 : out = 64'b0000000000001000000000000000000000000000000000000000000000000000; + 6'b110100 : out = 64'b0000000000010000000000000000000000000000000000000000000000000000; + 6'b110101 : out = 64'b0000000000100000000000000000000000000000000000000000000000000000; + 6'b110110 : out = 64'b0000000001000000000000000000000000000000000000000000000000000000; + 6'b110111 : out = 64'b0000000010000000000000000000000000000000000000000000000000000000; + 6'b111000 : out = 64'b0000000100000000000000000000000000000000000000000000000000000000; + 6'b111001 : out = 64'b0000001000000000000000000000000000000000000000000000000000000000; + 6'b111010 : out = 64'b0000010000000000000000000000000000000000000000000000000000000000; + 6'b111011 : out = 64'b0000100000000000000000000000000000000000000000000000000000000000; + 6'b111100 : out = 64'b0001000000000000000000000000000000000000000000000000000000000000; + 6'b111101 : out = 64'b0010000000000000000000000000000000000000000000000000000000000000; + 6'b111110 : out = 64'b0100000000000000000000000000000000000000000000000000000000000000; + 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000; + endcase + end +endmodule + + +module MUX2(input [1:0] in, input select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + endcase +endmodule + + +module MUX4(input [3:0] in, input [1:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + endcase +endmodule + + +module MUX8(input [7:0] in, input [2:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + endcase +endmodule + +module MUX16(input [15:0] in, input [3:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + endcase +endmodule + +module MUX32(input [31:0] in, input [4:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + 16: out = in[16]; + 17: out = in[17]; + 18: out = in[18]; + 19: out = in[19]; + 20: out = in[20]; + 21: out = in[21]; + 22: out = in[22]; + 23: out = in[23]; + 24: out = in[24]; + 25: out = in[25]; + 26: out = in[26]; + 27: out = in[27]; + 28: out = in[28]; + 29: out = in[29]; + 30: out = in[30]; + 31: out = in[31]; + endcase +endmodule + +module MUX64(input [63:0] in, input [5:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + 16: out = in[16]; + 17: out = in[17]; + 18: out = in[18]; + 19: out = in[19]; + 20: out = in[20]; + 21: out = in[21]; + 22: out = in[22]; + 23: out = in[23]; + 24: out = in[24]; + 25: out = in[25]; + 26: out = in[26]; + 27: out = in[27]; + 28: out = in[28]; + 29: out = in[29]; + 30: out = in[30]; + 31: out = in[31]; + 32: out = in[32]; + 33: out = in[33]; + 34: out = in[34]; + 35: out = in[35]; + 36: out = in[36]; + 37: out = in[37]; + 38: out = in[38]; + 39: out = in[39]; + 40: out = in[40]; + 41: out = in[41]; + 42: out = in[42]; + 43: out = in[43]; + 44: out = in[44]; + 45: out = in[45]; + 46: out = in[46]; + 47: out = in[47]; + 48: out = in[48]; + 49: out = in[49]; + 50: out = in[50]; + 51: out = in[51]; + 52: out = in[52]; + 53: out = in[53]; + 54: out = in[54]; + 55: out = in[55]; + 56: out = in[56]; + 57: out = in[57]; + 58: out = in[58]; + 59: out = in[59]; + 60: out = in[60]; + 61: out = in[61]; + 62: out = in[62]; + 63: out = in[63]; + endcase +endmodule + +module ADD1(input in1, in2, cin, output out, cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule + +module ADD2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule + +module ADD4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule + +module ADD8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule + +module ADD16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule + +module ADD32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule +module ADD64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 + in2 + cin; + +endmodule + +module SUB1(input in1, in2, cin, output out, cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule + +module SUB2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule + +module SUB4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule + +module SUB8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule + +module SUB16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule + +module SUB32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule +module SUB64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, + input cin, output [SIZE-1:0] out, output cout); + +assign {cout, out} = in1 - in2 - cin; + +endmodule + +module MUL1 #(parameter SIZE = 1)(input in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module MUL2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module MUL4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module MUL8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module MUL16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module MUL32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module MUL64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); + +assign out = in1*in2; + +endmodule + +module DIV1 #(parameter SIZE = 1)(input in1, in2, output out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module DIV2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, + output [SIZE-1:0] out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module DIV4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, + output [SIZE-1:0] out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module DIV8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, + output [SIZE-1:0] out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module DIV16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, + output [SIZE-1:0] out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module DIV32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, + output [SIZE-1:0] out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module DIV64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, + output [SIZE-1:0] out, rem); + +assign out = in1/in2; +assign rem = in1%in2; + +endmodule + +module FF (input d, clk, output reg q); +always @( posedge clk) + q <= d; +endmodule + + +module RFF(input d, clk, reset, output reg q); +always @(posedge clk or posedge reset) + if(reset) + q <= 0; + else + q <= d; +endmodule + +module SFF(input d, clk, set, output reg q); +always @(posedge clk or posedge set) + if(set) + q <= 1; + else + q <= d; +endmodule + +module RSFF(input d, clk, set, reset, output reg q); +always @(posedge clk or posedge reset or posedge set) + if(reset) + q <= 0; + else if(set) + q <= 1; + else + q <= d; +endmodule + +module SRFF(input d, clk, set, reset, output reg q); +always @(posedge clk or posedge set or posedge reset) + if(set) + q <= 1; + else if(reset) + q <= 0; + else + q <= d; +endmodule + +module LATCH(input d, enable, output reg q); +always @( d or enable) + if(enable) + q <= d; +endmodule + +module RLATCH(input d, reset, enable, output reg q); +always @( d or enable or reset) + if(enable) + if(reset) + q <= 0; + else + q <= d; +endmodule + +module LSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out); + +always @ (in, shift, val) begin + if(shift) + out = val; + else + out = in; +end + +endmodule + + +module LSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, + input [SIZE-1:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in << shift; + if(val) + out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); +end +endmodule + +module LSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, + input [2:0] shift, input val, output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in << shift; + if(val) + out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); +end +endmodule + + +module LSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, + input [3:0] shift, input val, output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in << shift; + if(val) + out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); +end +endmodule + +module LSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, + input [4:0] shift, input val, output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in << shift; + if(val) + out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); +end +endmodule + +module LSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, + input [5:0] shift, input val, output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in << shift; + if(val) + out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); +end +endmodule + +module LSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, + input [6:0] shift, input val, output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in << shift; + if(val) + out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); +end +endmodule + +module RSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out); + +always @ (in, shift, val) begin + if(shift) + out = val; + else + out = in; +end + +endmodule + +module RSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, + input [SIZE-1:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in >> shift; + if(val) + out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); +end + +endmodule + + +module RSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, + input [2:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in >> shift; + if(val) + out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); +end +endmodule + +module RSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, + input [3:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in >> shift; + if(val) + out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); +end + +endmodule + +module RSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, + input [4:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in >> shift; + if(val) + out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); +end +endmodule + + +module RSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, + input [5:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in >> shift; + if(val) + out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); +end +endmodule + +module RSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, + input [6:0] shift, input val, + output reg [SIZE-1:0] out); + +always @(in or shift or val) begin + out = in >> shift; + if(val) + out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); +end +endmodule + +module CMP1 #(parameter SIZE = 1) (input in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + + +module CMP2 #(parameter SIZE = 2) (input [SIZE-1:0] in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + +module CMP4 #(parameter SIZE = 4) (input [SIZE-1:0] in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + +module CMP8 #(parameter SIZE = 8) (input [SIZE-1:0] in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + +module CMP16 #(parameter SIZE = 16) (input [SIZE-1:0] in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + +module CMP32 #(parameter SIZE = 32) (input [SIZE-1:0] in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + +module CMP64 #(parameter SIZE = 64) (input [SIZE-1:0] in1, in2, + output reg equal, unequal, greater, lesser); + +always @ (in1 or in2) begin + if(in1 == in2) begin + equal = 1; + unequal = 0; + greater = 0; + lesser = 0; + end + else begin + equal = 0; + unequal = 1; + + if(in1 < in2) begin + greater = 0; + lesser = 1; + end + else begin + greater = 1; + lesser = 0; + end + end +end +endmodule + +module VCC (output supply1 out); +endmodule + +module GND (output supply0 out); +endmodule + + +module INC1 #(parameter SIZE = 1) (input in, output [SIZE:0] out); + +assign out = in + 1; + +endmodule + +module INC2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output [SIZE:0] out); + +assign out = in + 1; + +endmodule + +module INC4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output [SIZE:0] out); +assign out = in + 1; + +endmodule + +module INC8 #(parameter SIZE = 8) (input [SIZE-1:0] in, output [SIZE:0] out); +assign out = in + 1; + +endmodule + +module INC16 #(parameter SIZE = 16) (input [SIZE-1:0] in, output [SIZE:0] out); +assign out = in + 1; + +endmodule + +module INC32 #(parameter SIZE = 32) (input [SIZE-1:0] in, output [SIZE:0] out); +assign out = in + 1; + +endmodule +module INC64 #(parameter SIZE = 64) (input [SIZE-1:0] in, output [SIZE:0] out); +assign out = in + 1; + +endmodule + diff --git a/tests/hana/run-test.sh b/tests/hana/run-test.sh new file mode 100755 index 000000000..b8e7231c7 --- /dev/null +++ b/tests/hana/run-test.sh @@ -0,0 +1,3 @@ +#!/bin/bash +make -C ../.. || exit 1 +exec bash ../tools/autotest.sh -l hana_vlib.v test_*.v diff --git a/tests/hana/test_intermout_always_comb_1_test.v b/tests/hana/test_intermout_always_comb_1_test.v new file mode 100644 index 000000000..2d5abc4a6 --- /dev/null +++ b/tests/hana/test_intermout_always_comb_1_test.v @@ -0,0 +1,13 @@ +module test(a, b, c, d, z); +input a, b, c, d; +output z; +reg z, temp1, temp2; + +always @(a or b or c or d) +begin + temp1 = a ^ b; + temp2 = c ^ d; + z = temp1 ^ temp2; +end + +endmodule diff --git a/tests/hana/test_intermout_always_comb_3_test.v b/tests/hana/test_intermout_always_comb_3_test.v new file mode 100644 index 000000000..234407efd --- /dev/null +++ b/tests/hana/test_intermout_always_comb_3_test.v @@ -0,0 +1,10 @@ +module test (in1, in2, out); +input in1, in2; +output reg out; + +always @ ( in1 or in2) + if(in1 > in2) + out = in1; + else + out = in2; +endmodule diff --git a/tests/hana/test_intermout_always_comb_4_test.v b/tests/hana/test_intermout_always_comb_4_test.v new file mode 100644 index 000000000..b0a94f299 --- /dev/null +++ b/tests/hana/test_intermout_always_comb_4_test.v @@ -0,0 +1,9 @@ +module test(a, b, c); +input b, c; +output reg a; + +always @(b or c) begin +a = b; +a = c; +end +endmodule diff --git a/tests/hana/test_intermout_always_comb_5_test.v b/tests/hana/test_intermout_always_comb_5_test.v new file mode 100644 index 000000000..5152781df --- /dev/null +++ b/tests/hana/test_intermout_always_comb_5_test.v @@ -0,0 +1,11 @@ +module test(ctrl, in1, in2, out); +input ctrl; +input in1, in2; +output reg out; + +always @ (ctrl or in1 or in2) + if(ctrl) + out = in1 & in2; + else + out = in1 | in2; +endmodule diff --git a/tests/hana/test_intermout_always_ff_3_test.v b/tests/hana/test_intermout_always_ff_3_test.v new file mode 100644 index 000000000..ed8630c37 --- /dev/null +++ b/tests/hana/test_intermout_always_ff_3_test.v @@ -0,0 +1,15 @@ +module NonBlockingEx(clk, merge, er, xmit, fddi, claim); +input clk, merge, er, xmit, fddi; +output reg claim; +reg fcr; + +always @(posedge clk) +begin + fcr = er | xmit; + + if(merge) + claim = fcr & fddi; + else + claim = fddi; +end +endmodule diff --git a/tests/hana/test_intermout_always_ff_4_test.v b/tests/hana/test_intermout_always_ff_4_test.v new file mode 100644 index 000000000..cac420a47 --- /dev/null +++ b/tests/hana/test_intermout_always_ff_4_test.v @@ -0,0 +1,11 @@ +module FlipFlop(clk, cs, ns); +input clk; +input [31:0] cs; +output [31:0] ns; +integer is; + +always @(posedge clk) + is <= cs; + +assign ns = is; +endmodule diff --git a/tests/hana/test_intermout_always_ff_5_test.v b/tests/hana/test_intermout_always_ff_5_test.v new file mode 100644 index 000000000..669b2a5f9 --- /dev/null +++ b/tests/hana/test_intermout_always_ff_5_test.v @@ -0,0 +1,13 @@ +module FlipFlop(clock, cs, ns); +input clock; +input [3:0] cs; +output reg [3:0] ns; +reg [3:0] temp; + +always @(posedge clock) +begin + temp = cs; + ns = temp; +end + +endmodule diff --git a/tests/hana/test_intermout_always_ff_6_test.v b/tests/hana/test_intermout_always_ff_6_test.v new file mode 100644 index 000000000..ad0a0df6e --- /dev/null +++ b/tests/hana/test_intermout_always_ff_6_test.v @@ -0,0 +1,7 @@ +module inc(clock, counter); + +input clock; +output reg [3:0] counter; +always @(posedge clock) + counter <= counter + 1; +endmodule diff --git a/tests/hana/test_intermout_always_ff_8_test.v b/tests/hana/test_intermout_always_ff_8_test.v new file mode 100644 index 000000000..0f29ea0a4 --- /dev/null +++ b/tests/hana/test_intermout_always_ff_8_test.v @@ -0,0 +1,11 @@ +module NegEdgeClock(q, d, clk, reset); +input d, clk, reset; +output reg q; + +always @(negedge clk or negedge reset) + if(!reset) + q <= 1'b0; + else + q <= d; + +endmodule diff --git a/tests/hana/test_intermout_always_ff_9_test.v b/tests/hana/test_intermout_always_ff_9_test.v new file mode 100644 index 000000000..f1f13bbe7 --- /dev/null +++ b/tests/hana/test_intermout_always_ff_9_test.v @@ -0,0 +1,14 @@ +module MyCounter (clock, preset, updown, presetdata, counter); +input clock, preset, updown; +input [1: 0] presetdata; +output reg [1:0] counter; + +always @(posedge clock) + if(preset) + counter <= presetdata; + else + if(updown) + counter <= counter + 1; + else + counter <= counter - 1; +endmodule diff --git a/tests/hana/test_intermout_always_latch_1_test.v b/tests/hana/test_intermout_always_latch_1_test.v new file mode 100644 index 000000000..a83be20d1 --- /dev/null +++ b/tests/hana/test_intermout_always_latch_1_test.v @@ -0,0 +1,9 @@ +module test(en, in, out); +input en; +input [1:0] in; +output reg [2:0] out; + +always @ (en or in) + if(en) + out = in + 1; +endmodule diff --git a/tests/hana/test_intermout_bufrm_1_test.v b/tests/hana/test_intermout_bufrm_1_test.v new file mode 100644 index 000000000..8e3d4222e --- /dev/null +++ b/tests/hana/test_intermout_bufrm_1_test.v @@ -0,0 +1,4 @@ +module test(input in, output out); +//no buffer removal +assign out = in; +endmodule diff --git a/tests/hana/test_intermout_bufrm_2_test.v b/tests/hana/test_intermout_bufrm_2_test.v new file mode 100644 index 000000000..853f1dc9a --- /dev/null +++ b/tests/hana/test_intermout_bufrm_2_test.v @@ -0,0 +1,7 @@ +module test(input in, output out); +//intermediate buffers should be removed +wire w1, w2; +assign w1 = in; +assign w2 = w1; +assign out = w2; +endmodule diff --git a/tests/hana/test_intermout_bufrm_6_test.v b/tests/hana/test_intermout_bufrm_6_test.v new file mode 100644 index 000000000..d4f3878d5 --- /dev/null +++ b/tests/hana/test_intermout_bufrm_6_test.v @@ -0,0 +1,22 @@ +module test(in, out); +input in; +output out; + +wire w1, w2, w3, w4; +assign w1 = in; +assign w2 = w1; +assign w4 = w3; +assign out = w4; +mybuf _mybuf(w2, w3); +endmodule + +module mybuf(in, out); +input in; +output out; +wire w1, w2, w3, w4; + +assign w1 = in; +assign w2 = w1; +assign out = w2; +endmodule + diff --git a/tests/hana/test_intermout_bufrm_7_test.v b/tests/hana/test_intermout_bufrm_7_test.v new file mode 100644 index 000000000..7b651302a --- /dev/null +++ b/tests/hana/test_intermout_bufrm_7_test.v @@ -0,0 +1,33 @@ +module test(in1, in2, out); +input in1, in2; +output out; +// Y with cluster of mybuf instances at the junction + +wire w1, w2, w3, w4, w5, w6, w7, w8, w9, w10; +assign w1 = in1; +assign w2 = w1; +assign w5 = in2; +assign w6 = w5; +assign w10 = w9; +assign out = w10; + +mybuf _mybuf0(w2, w3); +mybuf _mybuf1(w3, w4); + +mybuf _mybuf2(w6, w7); +mybuf _mybuf3(w7, w4); + +mybuf _mybuf4(w4, w8); +mybuf _mybuf5(w8, w9); +endmodule + +module mybuf(in, out); +input in; +output out; +wire w1, w2, w3, w4; + +assign w1 = in; +assign w2 = w1; +assign out = w2; +endmodule + diff --git a/tests/hana/test_intermout_exprs_add_test.v b/tests/hana/test_intermout_exprs_add_test.v new file mode 100644 index 000000000..ec70f347b --- /dev/null +++ b/tests/hana/test_intermout_exprs_add_test.v @@ -0,0 +1,10 @@ +module test(out, in1, in2, vin1, vin2, vout1); +output out; +input in1, in2; +input [1:0] vin1; +input [2:0] vin2; +output [3:0] vout1; + +assign out = in1 + in2; +assign vout1 = vin1 + vin2; +endmodule diff --git a/tests/hana/test_intermout_exprs_binlogic_test.v b/tests/hana/test_intermout_exprs_binlogic_test.v new file mode 100644 index 000000000..eec8c4b1a --- /dev/null +++ b/tests/hana/test_intermout_exprs_binlogic_test.v @@ -0,0 +1,13 @@ +module test(in1, in2, vin1, vin2, out, vout, vin3, vin4, vout1 ); +input in1, in2; +input [1:0] vin1; +input [3:0] vin2; +input [1:0] vin3; +input [3:0] vin4; +output vout, vout1; +output out; + +assign out = in1 && in2; +assign vout = vin1 && vin2; +assign vout1 = vin3 || vin4; +endmodule diff --git a/tests/hana/test_intermout_exprs_bitwiseneg_test.v b/tests/hana/test_intermout_exprs_bitwiseneg_test.v new file mode 100644 index 000000000..5b62bef09 --- /dev/null +++ b/tests/hana/test_intermout_exprs_bitwiseneg_test.v @@ -0,0 +1,5 @@ +module test(output out, input in, output [1:0] vout, input [1:0] vin); + +assign out = ~in; +assign vout = ~vin; +endmodule diff --git a/tests/hana/test_intermout_exprs_buffer_test.v b/tests/hana/test_intermout_exprs_buffer_test.v new file mode 100644 index 000000000..2b4cbc3ed --- /dev/null +++ b/tests/hana/test_intermout_exprs_buffer_test.v @@ -0,0 +1,9 @@ +module buffer(in, out, vin, vout); +input in; +output out; +input [1:0] vin; +output [1:0] vout; + +assign out = in; +assign vout = vin; +endmodule diff --git a/tests/hana/test_intermout_exprs_condexpr_mux_test.v b/tests/hana/test_intermout_exprs_condexpr_mux_test.v new file mode 100644 index 000000000..11006e8b3 --- /dev/null +++ b/tests/hana/test_intermout_exprs_condexpr_mux_test.v @@ -0,0 +1,11 @@ +module test(in1, in2, out, vin1, vin2, vin3, vin4, vout1, vout2, en1, ven1, ven2); +input in1, in2, en1, ven1; +input [1:0] ven2; +output out; +input [1:0] vin1, vin2, vin3, vin4; +output [1:0] vout1, vout2; + +assign out = en1 ? in1 : in2; +assign vout1 = ven1 ? vin1 : vin2; +assign vout2 = ven2 ? vin3 : vin4; +endmodule diff --git a/tests/hana/test_intermout_exprs_condexpr_tribuf_test.v b/tests/hana/test_intermout_exprs_condexpr_tribuf_test.v new file mode 100644 index 000000000..5b778fe98 --- /dev/null +++ b/tests/hana/test_intermout_exprs_condexpr_tribuf_test.v @@ -0,0 +1,9 @@ +module test(in, out, en, vin1, vout1, en1); +input in, en, en1; +output out; +input [1:0] vin1; +output [1:0] vout1; + +assign out = en ? in : 1'bz; +assign vout1 = en1 ? vin1 : 2'bzz; +endmodule diff --git a/tests/hana/test_intermout_exprs_const_test.v b/tests/hana/test_intermout_exprs_const_test.v new file mode 100644 index 000000000..484d81032 --- /dev/null +++ b/tests/hana/test_intermout_exprs_const_test.v @@ -0,0 +1,7 @@ +module test (out, vout); +output out; +output [7:0] vout; + +assign out = 1'b1; +assign vout = 9; +endmodule diff --git a/tests/hana/test_intermout_exprs_constshift_test.v b/tests/hana/test_intermout_exprs_constshift_test.v new file mode 100644 index 000000000..eb21315d7 --- /dev/null +++ b/tests/hana/test_intermout_exprs_constshift_test.v @@ -0,0 +1,12 @@ +module test(in, out, vin, vout, vin1, vout1, vin2, vout2); + +input in; +input [3:0] vin, vin1, vin2; +output [3:0] vout, vout1, vout2; +output out; + +assign out = in << 1; +assign vout = vin << 2; +assign vout1 = vin1 >> 2; +assign vout2 = vin2 >>> 2; +endmodule diff --git a/tests/hana/test_intermout_exprs_div_test.v b/tests/hana/test_intermout_exprs_div_test.v new file mode 100644 index 000000000..21765fcdf --- /dev/null +++ b/tests/hana/test_intermout_exprs_div_test.v @@ -0,0 +1,10 @@ +module test(out, in1, in2, vin1, vin2, vout1); +output out; +input in1, in2; +input [1:0] vin1; +input [2:0] vin2; +output [3:0] vout1; + +assign out = in1 / in2; +assign vout1 = vin1 / vin2; +endmodule diff --git a/tests/hana/test_intermout_exprs_logicneg_test.v b/tests/hana/test_intermout_exprs_logicneg_test.v new file mode 100644 index 000000000..b45b32b9c --- /dev/null +++ b/tests/hana/test_intermout_exprs_logicneg_test.v @@ -0,0 +1,7 @@ +module test(out, vout, in, vin); +output out, vout; +input in; +input [3:0] vin; +assign out = !in; +assign vout = !vin; +endmodule diff --git a/tests/hana/test_intermout_exprs_mod_test.v b/tests/hana/test_intermout_exprs_mod_test.v new file mode 100644 index 000000000..cea6b02d7 --- /dev/null +++ b/tests/hana/test_intermout_exprs_mod_test.v @@ -0,0 +1,10 @@ +module test(out, in1, in2, vin1, vin2, vout1); +output out; +input in1, in2; +input [1:0] vin1; +input [2:0] vin2; +output [3:0] vout1; + +assign out = in1 % in2; +assign vout1 = vin1 % vin2; +endmodule diff --git a/tests/hana/test_intermout_exprs_mul_test.v b/tests/hana/test_intermout_exprs_mul_test.v new file mode 100644 index 000000000..f9973dadc --- /dev/null +++ b/tests/hana/test_intermout_exprs_mul_test.v @@ -0,0 +1,10 @@ +module test(out, in1, in2, vin1, vin2, vout1); +output out; +input in1, in2; +input [1:0] vin1; +input [2:0] vin2; +output [3:0] vout1; + +assign out = in1 * in2; +assign vout1 = vin1 * vin2; +endmodule diff --git a/tests/hana/test_intermout_exprs_redand_test.v b/tests/hana/test_intermout_exprs_redand_test.v new file mode 100644 index 000000000..35fdf73ac --- /dev/null +++ b/tests/hana/test_intermout_exprs_redand_test.v @@ -0,0 +1,5 @@ +module test(output out, input [1:0] vin, output out1, input [3:0] vin1); + +assign out = &vin; +assign out1 = &vin1; +endmodule diff --git a/tests/hana/test_intermout_exprs_redop_test.v b/tests/hana/test_intermout_exprs_redop_test.v new file mode 100644 index 000000000..93fdb2e57 --- /dev/null +++ b/tests/hana/test_intermout_exprs_redop_test.v @@ -0,0 +1,16 @@ +module Reduction (A1, A2, A3, A4, A5, A6, Y1, Y2, Y3, Y4, Y5, Y6); +input [1:0] A1; +input [1:0] A2; +input [1:0] A3; +input [1:0] A4; +input [1:0] A5; +input [1:0] A6; +output Y1, Y2, Y3, Y4, Y5, Y6; +//reg Y1, Y2, Y3, Y4, Y5, Y6; +assign Y1=&A1; //reduction AND +assign Y2=|A2; //reduction OR +assign Y3=~&A3; //reduction NAND +assign Y4=~|A4; //reduction NOR +assign Y5=^A5; //reduction XOR +assign Y6=~^A6; //reduction XNOR +endmodule diff --git a/tests/hana/test_intermout_exprs_sub_test.v b/tests/hana/test_intermout_exprs_sub_test.v new file mode 100644 index 000000000..06e3a8149 --- /dev/null +++ b/tests/hana/test_intermout_exprs_sub_test.v @@ -0,0 +1,10 @@ +module test(out, in1, in2, vin1, vin2, vout1); +output out; +input in1, in2; +input [1:0] vin1; +input [2:0] vin2; +output [3:0] vout1; + +assign out = in1 - in2; +assign vout1 = vin1 - vin2; +endmodule diff --git a/tests/hana/test_intermout_exprs_unaryminus_test.v b/tests/hana/test_intermout_exprs_unaryminus_test.v new file mode 100644 index 000000000..ee3f229a8 --- /dev/null +++ b/tests/hana/test_intermout_exprs_unaryminus_test.v @@ -0,0 +1,5 @@ +module test(output out, input in, output [31:0] vout, input [31:0] vin); + +assign out = -in; +assign vout = -vin; +endmodule diff --git a/tests/hana/test_intermout_exprs_unaryplus_test.v b/tests/hana/test_intermout_exprs_unaryplus_test.v new file mode 100644 index 000000000..07be5b240 --- /dev/null +++ b/tests/hana/test_intermout_exprs_unaryplus_test.v @@ -0,0 +1,4 @@ +module test(output out, input in); + +assign out = +in; +endmodule diff --git a/tests/hana/test_intermout_exprs_varshift_test.v b/tests/hana/test_intermout_exprs_varshift_test.v new file mode 100644 index 000000000..2ca35c091 --- /dev/null +++ b/tests/hana/test_intermout_exprs_varshift_test.v @@ -0,0 +1,10 @@ +module test(vin0, vout0); +input [2:0] vin0; +output reg [7:0] vout0; + +wire [7:0] myreg0, myreg1, myreg2; +integer i; +assign myreg0 = vout0 << vin0; + +assign myreg1 = myreg2 >> i; +endmodule diff --git a/tests/hana/test_parse2synthtrans_behavopt_1_test.v b/tests/hana/test_parse2synthtrans_behavopt_1_test.v new file mode 100644 index 000000000..c825739c6 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_behavopt_1_test.v @@ -0,0 +1,22 @@ +module test(in, out, clk, reset); +input in, reset; +output reg out; +input clk; +reg signed [3:0] a; +reg signed [3:0] b; +reg signed [3:0] c; +reg [5:0] d; +reg [5:0] e; + +always @(clk or reset) begin + a = -4; + b = 2; + c = a + b; + d = a + b + c; + d = d*d; + if(b) + e = d*d; + else + e = d + d; +end +endmodule diff --git a/tests/hana/test_parse2synthtrans_case_1_test.v b/tests/hana/test_parse2synthtrans_case_1_test.v new file mode 100644 index 000000000..348c566ad --- /dev/null +++ b/tests/hana/test_parse2synthtrans_case_1_test.v @@ -0,0 +1,26 @@ +module demultiplexer1_to_4 (out0, out1, out2, out3, in, s1, s0); +output out0, out1, out2, out3; +reg out0, out1, out2, out3; +input in; +input s1, s0; +reg [3:0] encoding; +reg [1:0] state; + always @(encoding) begin + case (encoding) + 4'bxx11: state = 1; + 4'bx0xx: state = 3; + 4'b11xx: state = 4; + 4'bx1xx: state = 2; + 4'bxx1x: state = 1; + 4'bxxx1: state = 0; + default: state = 0; + endcase + end + + always @(encoding) begin + case (encoding) + 4'b0000: state = 1; + default: state = 0; + endcase + end +endmodule diff --git a/tests/hana/test_parse2synthtrans_contassign_1_test.v b/tests/hana/test_parse2synthtrans_contassign_1_test.v new file mode 100644 index 000000000..78bf00775 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_contassign_1_test.v @@ -0,0 +1,7 @@ +module test(in, out); + +input wire in; +output out; +assign out = (in+in); +assign out = 74; +endmodule diff --git a/tests/hana/test_parse2synthtrans_module_basic0_test.v b/tests/hana/test_parse2synthtrans_module_basic0_test.v new file mode 100644 index 000000000..67a272df0 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_module_basic0_test.v @@ -0,0 +1,2 @@ +module test; +endmodule diff --git a/tests/hana/test_parse2synthtrans_operators_1_test.v b/tests/hana/test_parse2synthtrans_operators_1_test.v new file mode 100644 index 000000000..93b5691f3 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_operators_1_test.v @@ -0,0 +1,11 @@ +module test(in, out); +input in; +output out; +parameter p1 = 10; +parameter p2 = 5; + +assign out = +p1; +assign out = -p2; +assign out = p1 + p2; +assign out = p1 - p2; +endmodule diff --git a/tests/hana/test_parse2synthtrans_param_1_test.v b/tests/hana/test_parse2synthtrans_param_1_test.v new file mode 100644 index 000000000..146eedf42 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_param_1_test.v @@ -0,0 +1,7 @@ +module test(in, out); +input in; +output out; +parameter p = 10; + +assign out = p; +endmodule diff --git a/tests/hana/test_parse2synthtrans_port_scalar_1_test.v b/tests/hana/test_parse2synthtrans_port_scalar_1_test.v new file mode 100644 index 000000000..8cdf495a0 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_port_scalar_1_test.v @@ -0,0 +1,6 @@ +module test(in, out, io); +inout io; +output out; +input in; + +endmodule diff --git a/tests/hana/test_parse2synthtrans_port_vector_1_test.v b/tests/hana/test_parse2synthtrans_port_vector_1_test.v new file mode 100644 index 000000000..a740282b3 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_port_vector_1_test.v @@ -0,0 +1,9 @@ +module test(in1, in2, out1, out2, io1, io2); +inout [1:0] io1; +inout [0:1] io2; +output [1:0] out1; +output [0:1] out2; +input [1:0] in1; +input [0:1] in2; + +endmodule diff --git a/tests/hana/test_parse2synthtrans_v2k_comb_logic_sens_list_test.v b/tests/hana/test_parse2synthtrans_v2k_comb_logic_sens_list_test.v new file mode 100644 index 000000000..50f1d3531 --- /dev/null +++ b/tests/hana/test_parse2synthtrans_v2k_comb_logic_sens_list_test.v @@ -0,0 +1,9 @@ +module test(q, d, clk, reset); +output reg q; +input d, clk, reset; + +always @ (posedge clk, negedge reset) + if(!reset) q <= 0; + else q <= d; + +endmodule diff --git a/tests/hana/test_parser_constructs_module_basic1_test.v b/tests/hana/test_parser_constructs_module_basic1_test.v new file mode 100644 index 000000000..67a272df0 --- /dev/null +++ b/tests/hana/test_parser_constructs_module_basic1_test.v @@ -0,0 +1,2 @@ +module test; +endmodule diff --git a/tests/hana/test_parser_constructs_param_basic0_test.v b/tests/hana/test_parser_constructs_param_basic0_test.v new file mode 100644 index 000000000..fd679230e --- /dev/null +++ b/tests/hana/test_parser_constructs_param_basic0_test.v @@ -0,0 +1,10 @@ +module test #( parameter v2kparam = 5) +(in, out, io, vin, vout, vio); +input in; +output out; +inout io; +input [3:0] vin; +output [v2kparam:0] vout; +inout [0:3] vio; +parameter myparam = 10; +endmodule diff --git a/tests/hana/test_parser_constructs_port_basic0_test.v b/tests/hana/test_parser_constructs_port_basic0_test.v new file mode 100644 index 000000000..8478e31da --- /dev/null +++ b/tests/hana/test_parser_constructs_port_basic0_test.v @@ -0,0 +1,8 @@ +module test(in, out, io, vin, vout, vio); +input in; +output out; +inout io; +input [3:0] vin; +output [3:0] vout; +inout [0:3] vio; +endmodule diff --git a/tests/hana/test_parser_directives_define_simpledef_test.v b/tests/hana/test_parser_directives_define_simpledef_test.v new file mode 100644 index 000000000..4a5d2345c --- /dev/null +++ b/tests/hana/test_parser_directives_define_simpledef_test.v @@ -0,0 +1,9 @@ +`define parvez ahmad +`define WIRE wire +`define TEN 10 + +module `parvez(); +parameter param = `TEN; +`WIRE w; +assign w = `TEN; +endmodule diff --git a/tests/hana/test_parser_misc_operators_test.v b/tests/hana/test_parser_misc_operators_test.v new file mode 100644 index 000000000..8fe8e7bad --- /dev/null +++ b/tests/hana/test_parser_misc_operators_test.v @@ -0,0 +1,29 @@ +module test(out, i0, i1, i2, i3, s1, s0); +output out; +input i0, i1, i2, i3; +input s1, s0; + +assign out = (~s1 & s0 & i0) | + (~s1 & s0 & i1) | + (s1 & ~s0 & i2) | + (s1 & s0 & i3); + +endmodule + +module ternaryop(out, i0, i1, i2, i3, s1, s0); +output out; +input i0, i1, i2, i3; +input s1, s0; + +assign out = s1 ? (s0 ? i3 : i2) : (s0 ? i1 : i0); + +endmodule + +module fulladd4(sum, c_out, a, b, c_in); +output [3:0] sum; +output c_out; +input [3:0] a, b; +input c_in; + +assign {c_out, sum} = a + b + c_in; +endmodule diff --git a/tests/hana/test_parser_v2k_comb_port_data_type_test.v b/tests/hana/test_parser_v2k_comb_port_data_type_test.v new file mode 100644 index 000000000..099585b56 --- /dev/null +++ b/tests/hana/test_parser_v2k_comb_port_data_type_test.v @@ -0,0 +1,6 @@ +module adder(sum , co, a, b, ci); +output reg [31:0] sum; +output reg co; +input wire [31:0] a, b; +input wire ci; +endmodule diff --git a/tests/hana/test_parser_v2k_comma_sep_sens_list_test.v b/tests/hana/test_parser_v2k_comma_sep_sens_list_test.v new file mode 100644 index 000000000..50f1d3531 --- /dev/null +++ b/tests/hana/test_parser_v2k_comma_sep_sens_list_test.v @@ -0,0 +1,9 @@ +module test(q, d, clk, reset); +output reg q; +input d, clk, reset; + +always @ (posedge clk, negedge reset) + if(!reset) q <= 0; + else q <= d; + +endmodule diff --git a/tests/hana/test_simulation_always_15_test.v b/tests/hana/test_simulation_always_15_test.v new file mode 100644 index 000000000..5c5fed5b6 --- /dev/null +++ b/tests/hana/test_simulation_always_15_test.v @@ -0,0 +1,5 @@ +module test(input [1:0] in, output reg [1:0] out); + +always @(in) + out = in; +endmodule diff --git a/tests/hana/test_simulation_always_17_test.v b/tests/hana/test_simulation_always_17_test.v new file mode 100644 index 000000000..2d5abc4a6 --- /dev/null +++ b/tests/hana/test_simulation_always_17_test.v @@ -0,0 +1,13 @@ +module test(a, b, c, d, z); +input a, b, c, d; +output z; +reg z, temp1, temp2; + +always @(a or b or c or d) +begin + temp1 = a ^ b; + temp2 = c ^ d; + z = temp1 ^ temp2; +end + +endmodule diff --git a/tests/hana/test_simulation_always_18_test.v b/tests/hana/test_simulation_always_18_test.v new file mode 100644 index 000000000..234407efd --- /dev/null +++ b/tests/hana/test_simulation_always_18_test.v @@ -0,0 +1,10 @@ +module test (in1, in2, out); +input in1, in2; +output reg out; + +always @ ( in1 or in2) + if(in1 > in2) + out = in1; + else + out = in2; +endmodule diff --git a/tests/hana/test_simulation_always_19_test.v b/tests/hana/test_simulation_always_19_test.v new file mode 100644 index 000000000..5152781df --- /dev/null +++ b/tests/hana/test_simulation_always_19_test.v @@ -0,0 +1,11 @@ +module test(ctrl, in1, in2, out); +input ctrl; +input in1, in2; +output reg out; + +always @ (ctrl or in1 or in2) + if(ctrl) + out = in1 & in2; + else + out = in1 | in2; +endmodule diff --git a/tests/hana/test_simulation_always_1_test.v b/tests/hana/test_simulation_always_1_test.v new file mode 100644 index 000000000..211369cb6 --- /dev/null +++ b/tests/hana/test_simulation_always_1_test.v @@ -0,0 +1,5 @@ +module test(input in, output reg out); + +always @(in) + out = in; +endmodule diff --git a/tests/hana/test_simulation_always_20_test.v b/tests/hana/test_simulation_always_20_test.v new file mode 100644 index 000000000..6b3e861dc --- /dev/null +++ b/tests/hana/test_simulation_always_20_test.v @@ -0,0 +1,15 @@ +module NonBlockingEx(clk, merge, er, xmit, fddi, claim); +input clk, merge, er, xmit, fddi; +output reg claim; +reg fcr; + +always @(posedge clk) +begin + fcr <= er | xmit; + + if(merge) + claim <= fcr & fddi; + else + claim <= fddi; +end +endmodule diff --git a/tests/hana/test_simulation_always_21_test.v b/tests/hana/test_simulation_always_21_test.v new file mode 100644 index 000000000..6c47b4bdf --- /dev/null +++ b/tests/hana/test_simulation_always_21_test.v @@ -0,0 +1,11 @@ +module FlipFlop(clk, cs, ns); +input clk; +input [7:0] cs; +output [7:0] ns; +integer is; + +always @(posedge clk) + is <= cs; + +assign ns = is; +endmodule diff --git a/tests/hana/test_simulation_always_22_test.v b/tests/hana/test_simulation_always_22_test.v new file mode 100644 index 000000000..8d91f8154 --- /dev/null +++ b/tests/hana/test_simulation_always_22_test.v @@ -0,0 +1,7 @@ +module inc(clock, counter); + +input clock; +output reg [7:0] counter; +always @(posedge clock) + counter <= counter + 1; +endmodule diff --git a/tests/hana/test_simulation_always_23_test.v b/tests/hana/test_simulation_always_23_test.v new file mode 100644 index 000000000..f1f13bbe7 --- /dev/null +++ b/tests/hana/test_simulation_always_23_test.v @@ -0,0 +1,14 @@ +module MyCounter (clock, preset, updown, presetdata, counter); +input clock, preset, updown; +input [1: 0] presetdata; +output reg [1:0] counter; + +always @(posedge clock) + if(preset) + counter <= presetdata; + else + if(updown) + counter <= counter + 1; + else + counter <= counter - 1; +endmodule diff --git a/tests/hana/test_simulation_always_27_test.v b/tests/hana/test_simulation_always_27_test.v new file mode 100644 index 000000000..577378fd6 --- /dev/null +++ b/tests/hana/test_simulation_always_27_test.v @@ -0,0 +1,13 @@ +module FlipFlop(clock, cs, ns); +input clock; +input cs; +output reg ns; +reg temp; + +always @(posedge clock) +begin + temp <= cs; + ns <= temp; +end + +endmodule diff --git a/tests/hana/test_simulation_always_29_test.v b/tests/hana/test_simulation_always_29_test.v new file mode 100644 index 000000000..55606832a --- /dev/null +++ b/tests/hana/test_simulation_always_29_test.v @@ -0,0 +1,9 @@ +module test(input in, output reg [1:0] out); + + always @(in) + begin + out = in; + out = out + in; + end + +endmodule diff --git a/tests/hana/test_simulation_always_31_tt.v b/tests/hana/test_simulation_always_31_tt.v new file mode 100644 index 000000000..299c0ca46 --- /dev/null +++ b/tests/hana/test_simulation_always_31_tt.v @@ -0,0 +1,50 @@ +module test(clk, cond, data); +input cond; +input clk; +output data; + +wire synth_net; +wire synth_net_0; +wire synth_net_1; +wire synth_net_2; + +wire synth_net_3; +wire synth_net_4; +wire synth_net_5; +wire synth_net_6; + +wire synth_net_7; +wire synth_net_8; +wire synth_net_9; +wire synth_net_10; + +wire synth_net_11; +wire tmp; +AND2 synth_AND(.in({synth_net_0, synth_net_1}), . + out(synth_net_2)); +AND2 synth_AND_0(.in({synth_net_3, synth_net_4}), .out( + synth_net_5)); +AND2 synth_AND_1(.in({synth_net_6, synth_net_7}), .out( + synth_net_8)); +AND2 synth_AND_2(.in({synth_net_9, synth_net_10}), .out( + synth_net_11)); +BUF synth_BUF(.in(synth_net), .out(synth_net_0)); +BUF + synth_BUF_0(.in(data), .out(synth_net_3)); +BUF synth_BUF_1(.in(synth_net_8) + , .out(tmp)); +BUF synth_BUF_2(.in(tmp), .out(synth_net_9)); +MUX2 synth_MUX(. + in({synth_net_2, synth_net_5}), .select(cond), .out(synth_net_6)); +MUX2 + synth_MUX_0(.in({synth_net_1, synth_net_4}), .select(cond), .out(synth_net_7 + )); +FF synth_FF(.d(synth_net_11), .clk(clk), .q(data)); +VCC synth_VCC(.out( + synth_net)); +VCC synth_VCC_0(.out(synth_net_1)); +VCC synth_VCC_1(.out( + synth_net_4)); +VCC synth_VCC_2(.out(synth_net_10)); +endmodule + diff --git a/tests/hana/test_simulation_and_1_test.v b/tests/hana/test_simulation_and_1_test.v new file mode 100644 index 000000000..fba639ca8 --- /dev/null +++ b/tests/hana/test_simulation_and_1_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = in[0] & in[1]; +endmodule diff --git a/tests/hana/test_simulation_and_2_test.v b/tests/hana/test_simulation_and_2_test.v new file mode 100644 index 000000000..715bc7ca6 --- /dev/null +++ b/tests/hana/test_simulation_and_2_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = in[0] && in[1]; +endmodule diff --git a/tests/hana/test_simulation_and_3_test.v b/tests/hana/test_simulation_and_3_test.v new file mode 100644 index 000000000..74dccabf8 --- /dev/null +++ b/tests/hana/test_simulation_and_3_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = in[0] & in[1] & in[2]; +endmodule diff --git a/tests/hana/test_simulation_and_4_test.v b/tests/hana/test_simulation_and_4_test.v new file mode 100644 index 000000000..48ed9102a --- /dev/null +++ b/tests/hana/test_simulation_and_4_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = in[0] && in[1] && in[2]; +endmodule diff --git a/tests/hana/test_simulation_and_5_test.v b/tests/hana/test_simulation_and_5_test.v new file mode 100644 index 000000000..29a355786 --- /dev/null +++ b/tests/hana/test_simulation_and_5_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = in[0] & in[1] & in[2] & in[3]; +endmodule diff --git a/tests/hana/test_simulation_and_6_test.v b/tests/hana/test_simulation_and_6_test.v new file mode 100644 index 000000000..ebce4eebf --- /dev/null +++ b/tests/hana/test_simulation_and_6_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = in[0] && in[1] && in[2] && in[3]; +endmodule diff --git a/tests/hana/test_simulation_and_7_test.v b/tests/hana/test_simulation_and_7_test.v new file mode 100644 index 000000000..d394adad7 --- /dev/null +++ b/tests/hana/test_simulation_and_7_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +and myand(out, in[0], in[1], in[2], in[3]); +endmodule diff --git a/tests/hana/test_simulation_buffer_1_test.v b/tests/hana/test_simulation_buffer_1_test.v new file mode 100644 index 000000000..e9bb7f617 --- /dev/null +++ b/tests/hana/test_simulation_buffer_1_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = in; +endmodule diff --git a/tests/hana/test_simulation_buffer_2_test.v b/tests/hana/test_simulation_buffer_2_test.v new file mode 100644 index 000000000..9a3f5aa3a --- /dev/null +++ b/tests/hana/test_simulation_buffer_2_test.v @@ -0,0 +1,4 @@ +module test(input [1:0] in, output [1:0] out); +assign out[0] = in[0]; +assign out[1] = in[1]; +endmodule diff --git a/tests/hana/test_simulation_buffer_3_test.v b/tests/hana/test_simulation_buffer_3_test.v new file mode 100644 index 000000000..9bca426d1 --- /dev/null +++ b/tests/hana/test_simulation_buffer_3_test.v @@ -0,0 +1,4 @@ +module test(input in, output [1:0] out); +assign out[0] = in; +assign out[1] = in; +endmodule diff --git a/tests/hana/test_simulation_decoder_2_test.v b/tests/hana/test_simulation_decoder_2_test.v new file mode 100644 index 000000000..5bdf19717 --- /dev/null +++ b/tests/hana/test_simulation_decoder_2_test.v @@ -0,0 +1,14 @@ +module test (input [1:0] in, input enable, output reg out); + +always @(in or enable) + if(!enable) + out = 4'b0000; + else begin + case (in) + 2'b00 : out = 0 ; + 2'b01 : out = 1; + 2'b10 : out = 0; + 2'b11 : out = 1; + endcase + end +endmodule diff --git a/tests/hana/test_simulation_decoder_3_test.v b/tests/hana/test_simulation_decoder_3_test.v new file mode 100644 index 000000000..44f5de12b --- /dev/null +++ b/tests/hana/test_simulation_decoder_3_test.v @@ -0,0 +1,14 @@ +module test (input [1:0] in, input enable, output reg [2:0] out); + +always @(in or enable) + if(!enable) + out = 3'b000; + else begin + case (in) + 2'b00 : out = 3'b001 ; + 2'b01 : out = 3'b010; + 2'b10 : out = 3'b010; + 2'b11 : out = 3'b100; + endcase + end +endmodule diff --git a/tests/hana/test_simulation_decoder_4_test.v b/tests/hana/test_simulation_decoder_4_test.v new file mode 100644 index 000000000..871a5bbfd --- /dev/null +++ b/tests/hana/test_simulation_decoder_4_test.v @@ -0,0 +1,14 @@ +module test (input [2:0] in, output reg [7:0] out); + +always @(in ) + case (in) + 3'b000 : out = 8'b00000001; + 3'b001 : out = 8'b00000010; + 3'b010 : out = 8'b00000100; + 3'b011 : out = 8'b00001000; + 3'b100 : out = 8'b00010000; + 3'b101 : out = 8'b00100000; + 3'b110 : out = 8'b01000000; + 3'b111 : out = 8'b10000000; + endcase +endmodule diff --git a/tests/hana/test_simulation_decoder_5_test.v b/tests/hana/test_simulation_decoder_5_test.v new file mode 100644 index 000000000..497fa4bfd --- /dev/null +++ b/tests/hana/test_simulation_decoder_5_test.v @@ -0,0 +1,17 @@ +module test (input [2:0] in, input enable, output reg [7:0] out); + +always @(in or enable ) + if(!enable) + out = 8'b00000000; + else + case (in) + 3'b000 : out = 8'b00000001; + 3'b001 : out = 8'b00000010; + 3'b010 : out = 8'b00000100; + 3'b011 : out = 8'b00001000; + 3'b100 : out = 8'b00010000; + 3'b101 : out = 8'b00100000; + 3'b110 : out = 8'b01000000; + 3'b111 : out = 8'b10000000; + endcase +endmodule diff --git a/tests/hana/test_simulation_decoder_6_test.v b/tests/hana/test_simulation_decoder_6_test.v new file mode 100644 index 000000000..fd19ad609 --- /dev/null +++ b/tests/hana/test_simulation_decoder_6_test.v @@ -0,0 +1,27 @@ +module test (input [3:0] in, input enable, output reg [15:0] out); + +always @(in or enable) + if(!enable) + out = 16'b0000000000000000; + else begin + case (in) + 4'b0000 : out = 16'b0000000000000001; + 4'b0001 : out = 16'b0000000000000010; + 4'b0010 : out = 16'b0000000000000100; + 4'b0011 : out = 16'b0000000000001000; + 4'b0100 : out = 16'b0000000000010000; + 4'b0101 : out = 16'b0000000000100000; + 4'b0110 : out = 16'b0000000001000000; + 4'b0111 : out = 16'b0000000010000000; + 4'b1000 : out = 16'b0000000100000000; + 4'b1001 : out = 16'b0000001000000000; + 4'b1010 : out = 16'b0000010000000000; + 4'b1011 : out = 16'b0000100000000000; + 4'b1100 : out = 16'b0001000000000000; + 4'b1101 : out = 16'b0010000000000000; + 4'b1110 : out = 16'b0100000000000000; + 4'b1111 : out = 16'b1000000000000000; + endcase + end +endmodule + diff --git a/tests/hana/test_simulation_decoder_7_test.v b/tests/hana/test_simulation_decoder_7_test.v new file mode 100644 index 000000000..462e94199 --- /dev/null +++ b/tests/hana/test_simulation_decoder_7_test.v @@ -0,0 +1,43 @@ +module test (input [4:0] in, input enable, output reg [31:0] out); + +always @(in or enable) + if(!enable) + out = 32'b00000000000000000000000000000000; + else begin + case (in) + 5'b00000 : out = 32'b00000000000000000000000000000001; + 5'b00001 : out = 32'b00000000000000000000000000000010; + 5'b00010 : out = 32'b00000000000000000000000000000100; + 5'b00011 : out = 32'b00000000000000000000000000001000; + 5'b00100 : out = 32'b00000000000000000000000000010000; + 5'b00101 : out = 32'b00000000000000000000000000100000; + 5'b00110 : out = 32'b00000000000000000000000001000000; + 5'b00111 : out = 32'b00000000000000000000000010000000; + 5'b01000 : out = 32'b00000000000000000000000100000000; + 5'b01001 : out = 32'b00000000000000000000001000000000; + 5'b01010 : out = 32'b00000000000000000000010000000000; + 5'b01011 : out = 32'b00000000000000000000100000000000; + 5'b01100 : out = 32'b00000000000000000001000000000000; + 5'b01101 : out = 32'b00000000000000000010000000000000; + 5'b01110 : out = 32'b00000000000000000100000000000000; + 5'b01111 : out = 32'b00000000000000001000000000000000; + 5'b10000 : out = 32'b00000000000000010000000000000000; + 5'b10001 : out = 32'b00000000000000100000000000000000; + 5'b10010 : out = 32'b00000000000001000000000000000000; + 5'b10011 : out = 32'b00000000000010000000000000000000; + 5'b10100 : out = 32'b00000000000100000000000000000000; + 5'b10101 : out = 32'b00000000001000000000000000000000; + 5'b10110 : out = 32'b00000000010000000000000000000000; + 5'b10111 : out = 32'b00000000100000000000000000000000; + 5'b11000 : out = 32'b00000001000000000000000000000000; + 5'b11001 : out = 32'b00000010000000000000000000000000; + 5'b11010 : out = 32'b00000100000000000000000000000000; + 5'b11011 : out = 32'b00001000000000000000000000000000; + 5'b11100 : out = 32'b00010000000000000000000000000000; + 5'b11101 : out = 32'b00100000000000000000000000000000; + 5'b11110 : out = 32'b01000000000000000000000000000000; + 5'b11111 : out = 32'b10000000000000000000000000000000; + endcase + end +endmodule + diff --git a/tests/hana/test_simulation_decoder_8_test.v b/tests/hana/test_simulation_decoder_8_test.v new file mode 100644 index 000000000..751d60f67 --- /dev/null +++ b/tests/hana/test_simulation_decoder_8_test.v @@ -0,0 +1,76 @@ +module test (input [5:0] in, input enable, output reg [63:0] out); + +always @(in or enable) + if(!enable) + out = 64'b0000000000000000000000000000000000000000000000000000000000000000; + else begin + case (in) + 6'b000000 : out = 64'b0000000000000000000000000000000000000000000000000000000000000001; + 6'b000001 : out = 64'b0000000000000000000000000000000000000000000000000000000000000010; + 6'b000010 : out = 64'b0000000000000000000000000000000000000000000000000000000000000100; + 6'b000011 : out = 64'b0000000000000000000000000000000000000000000000000000000000001000; + 6'b000100 : out = 64'b0000000000000000000000000000000000000000000000000000000000010000; + 6'b000101 : out = 64'b0000000000000000000000000000000000000000000000000000000000100000; + 6'b000110 : out = 64'b0000000000000000000000000000000000000000000000000000000001000000; + 6'b000111 : out = 64'b0000000000000000000000000000000000000000000000000000000010000000; + 6'b001000 : out = 64'b0000000000000000000000000000000000000000000000000000000100000000; + 6'b001001 : out = 64'b0000000000000000000000000000000000000000000000000000001000000000; + 6'b001010 : out = 64'b0000000000000000000000000000000000000000000000000000010000000000; + 6'b001011 : out = 64'b0000000000000000000000000000000000000000000000000000100000000000; + 6'b001100 : out = 64'b0000000000000000000000000000000000000000000000000001000000000000; + 6'b001101 : out = 64'b0000000000000000000000000000000000000000000000000010000000000000; + 6'b001110 : out = 64'b0000000000000000000000000000000000000000000000000100000000000000; + 6'b001111 : out = 64'b0000000000000000000000000000000000000000000000001000000000000000; + 6'b010000 : out = 64'b0000000000000000000000000000000000000000000000010000000000000000; + 6'b010001 : out = 64'b0000000000000000000000000000000000000000000000100000000000000000; + 6'b010010 : out = 64'b0000000000000000000000000000000000000000000001000000000000000000; + 6'b010011 : out = 64'b0000000000000000000000000000000000000000000010000000000000000000; + 6'b010100 : out = 64'b0000000000000000000000000000000000000000000100000000000000000000; + 6'b010101 : out = 64'b0000000000000000000000000000000000000000001000000000000000000000; + 6'b010110 : out = 64'b0000000000000000000000000000000000000000010000000000000000000000; + 6'b010111 : out = 64'b0000000000000000000000000000000000000000100000000000000000000000; + 6'b011000 : out = 64'b0000000000000000000000000000000000000001000000000000000000000000; + 6'b011001 : out = 64'b0000000000000000000000000000000000000010000000000000000000000000; + 6'b011010 : out = 64'b0000000000000000000000000000000000000100000000000000000000000000; + 6'b011011 : out = 64'b0000000000000000000000000000000000001000000000000000000000000000; + 6'b011100 : out = 64'b0000000000000000000000000000000000010000000000000000000000000000; + 6'b011101 : out = 64'b0000000000000000000000000000000000100000000000000000000000000000; + 6'b011110 : out = 64'b0000000000000000000000000000000001000000000000000000000000000000; + 6'b011111 : out = 64'b0000000000000000000000000000000010000000000000000000000000000000; + + 6'b100000 : out = 64'b0000000000000000000000000000000100000000000000000000000000000000; + 6'b100001 : out = 64'b0000000000000000000000000000001000000000000000000000000000000000; + 6'b100010 : out = 64'b0000000000000000000000000000010000000000000000000000000000000000; + 6'b100011 : out = 64'b0000000000000000000000000000100000000000000000000000000000000000; + 6'b100100 : out = 64'b0000000000000000000000000001000000000000000000000000000000000000; + 6'b100101 : out = 64'b0000000000000000000000000010000000000000000000000000000000000000; + 6'b100110 : out = 64'b0000000000000000000000000100000000000000000000000000000000000000; + 6'b100111 : out = 64'b0000000000000000000000001000000000000000000000000000000000000000; + 6'b101000 : out = 64'b0000000000000000000000010000000000000000000000000000000000000000; + 6'b101001 : out = 64'b0000000000000000000000100000000000000000000000000000000000000000; + 6'b101010 : out = 64'b0000000000000000000001000000000000000000000000000000000000000000; + 6'b101011 : out = 64'b0000000000000000000010000000000000000000000000000000000000000000; + 6'b101100 : out = 64'b0000000000000000000100000000000000000000000000000000000000000000; + 6'b101101 : out = 64'b0000000000000000001000000000000000000000000000000000000000000000; + 6'b101110 : out = 64'b0000000000000000010000000000000000000000000000000000000000000000; + 6'b101111 : out = 64'b0000000000000000100000000000000000000000000000000000000000000000; + 6'b110000 : out = 64'b0000000000000001000000000000000000000000000000000000000000000000; + 6'b110001 : out = 64'b0000000000000010000000000000000000000000000000000000000000000000; + 6'b110010 : out = 64'b0000000000000100000000000000000000000000000000000000000000000000; + 6'b110011 : out = 64'b0000000000001000000000000000000000000000000000000000000000000000; + 6'b110100 : out = 64'b0000000000010000000000000000000000000000000000000000000000000000; + 6'b110101 : out = 64'b0000000000100000000000000000000000000000000000000000000000000000; + 6'b110110 : out = 64'b0000000001000000000000000000000000000000000000000000000000000000; + 6'b110111 : out = 64'b0000000010000000000000000000000000000000000000000000000000000000; + 6'b111000 : out = 64'b0000000100000000000000000000000000000000000000000000000000000000; + 6'b111001 : out = 64'b0000001000000000000000000000000000000000000000000000000000000000; + 6'b111010 : out = 64'b0000010000000000000000000000000000000000000000000000000000000000; + 6'b111011 : out = 64'b0000100000000000000000000000000000000000000000000000000000000000; + 6'b111100 : out = 64'b0001000000000000000000000000000000000000000000000000000000000000; + 6'b111101 : out = 64'b0010000000000000000000000000000000000000000000000000000000000000; + 6'b111110 : out = 64'b0100000000000000000000000000000000000000000000000000000000000000; + 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000; + endcase + end +endmodule + diff --git a/tests/hana/test_simulation_inc_16_test.v b/tests/hana/test_simulation_inc_16_test.v new file mode 100644 index 000000000..7ff42ff50 --- /dev/null +++ b/tests/hana/test_simulation_inc_16_test.v @@ -0,0 +1,5 @@ +module test(input [15:0] in, output [15:0] out); + +assign out = -in; + +endmodule diff --git a/tests/hana/test_simulation_inc_1_test.v b/tests/hana/test_simulation_inc_1_test.v new file mode 100644 index 000000000..02bec2c27 --- /dev/null +++ b/tests/hana/test_simulation_inc_1_test.v @@ -0,0 +1,5 @@ +module test(input in, output out); + +assign out = -in; + +endmodule diff --git a/tests/hana/test_simulation_inc_2_test.v b/tests/hana/test_simulation_inc_2_test.v new file mode 100644 index 000000000..b96e05a2d --- /dev/null +++ b/tests/hana/test_simulation_inc_2_test.v @@ -0,0 +1,5 @@ +module test(input [1:0] in, output [1:0] out); + +assign out = -in; + +endmodule diff --git a/tests/hana/test_simulation_inc_32_test.v b/tests/hana/test_simulation_inc_32_test.v new file mode 100644 index 000000000..5700d0ce7 --- /dev/null +++ b/tests/hana/test_simulation_inc_32_test.v @@ -0,0 +1,5 @@ +module test(input [31:0] in, output [31:0] out); + +assign out = -in; + +endmodule diff --git a/tests/hana/test_simulation_inc_4_test.v b/tests/hana/test_simulation_inc_4_test.v new file mode 100644 index 000000000..34940d63e --- /dev/null +++ b/tests/hana/test_simulation_inc_4_test.v @@ -0,0 +1,5 @@ +module test(input [3:0] in, output [3:0] out); + +assign out = -in; + +endmodule diff --git a/tests/hana/test_simulation_inc_8_test.v b/tests/hana/test_simulation_inc_8_test.v new file mode 100644 index 000000000..c36d69f07 --- /dev/null +++ b/tests/hana/test_simulation_inc_8_test.v @@ -0,0 +1,5 @@ +module test(input [7:0] in, output [7:0] out); + +assign out = -in; + +endmodule diff --git a/tests/hana/test_simulation_mod_1_xx.v b/tests/hana/test_simulation_mod_1_xx.v new file mode 100644 index 000000000..75144a8e5 --- /dev/null +++ b/tests/hana/test_simulation_mod_1_xx.v @@ -0,0 +1,13 @@ +module test(in1, in2, out); +input in1; +input in2; +output out; + +wire synth_net_0; +wire synth_net_1; +BUF synth_BUF_0(.in(synth_net_1), .out(out + )); +DIV1 synth_DIV(.in1(in1), .in2(in2), .rem(synth_net_0), .out(synth_net_1 + )); +endmodule + diff --git a/tests/hana/test_simulation_mux_16_test.v b/tests/hana/test_simulation_mux_16_test.v new file mode 100644 index 000000000..de4b6f8e9 --- /dev/null +++ b/tests/hana/test_simulation_mux_16_test.v @@ -0,0 +1,22 @@ +module test(input [15:0] in, input [3:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + endcase +endmodule diff --git a/tests/hana/test_simulation_mux_2_test.v b/tests/hana/test_simulation_mux_2_test.v new file mode 100644 index 000000000..bc676c70b --- /dev/null +++ b/tests/hana/test_simulation_mux_2_test.v @@ -0,0 +1,8 @@ +module test(input [1:0] in, input select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + endcase +endmodule diff --git a/tests/hana/test_simulation_mux_32_test.v b/tests/hana/test_simulation_mux_32_test.v new file mode 100644 index 000000000..16de4d7f7 --- /dev/null +++ b/tests/hana/test_simulation_mux_32_test.v @@ -0,0 +1,39 @@ +module test(input [31:0] in, input [4:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + 16: out = in[16]; + 17: out = in[17]; + 18: out = in[18]; + 19: out = in[19]; + 20: out = in[20]; + 21: out = in[21]; + 22: out = in[22]; + 23: out = in[23]; + 24: out = in[24]; + 25: out = in[25]; + 26: out = in[26]; + 27: out = in[27]; + 28: out = in[28]; + 29: out = in[29]; + 30: out = in[30]; + 31: out = in[31]; + endcase +endmodule + diff --git a/tests/hana/test_simulation_mux_4_test.v b/tests/hana/test_simulation_mux_4_test.v new file mode 100644 index 000000000..6a112c6a9 --- /dev/null +++ b/tests/hana/test_simulation_mux_4_test.v @@ -0,0 +1,10 @@ +module test(input [3:0] in, input [1:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + endcase +endmodule diff --git a/tests/hana/test_simulation_mux_64_test.v b/tests/hana/test_simulation_mux_64_test.v new file mode 100644 index 000000000..420239c6e --- /dev/null +++ b/tests/hana/test_simulation_mux_64_test.v @@ -0,0 +1,71 @@ +module test(input [63:0] in, input [5:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + 16: out = in[16]; + 17: out = in[17]; + 18: out = in[18]; + 19: out = in[19]; + 20: out = in[20]; + 21: out = in[21]; + 22: out = in[22]; + 23: out = in[23]; + 24: out = in[24]; + 25: out = in[25]; + 26: out = in[26]; + 27: out = in[27]; + 28: out = in[28]; + 29: out = in[29]; + 30: out = in[30]; + 31: out = in[31]; + 32: out = in[32]; + 33: out = in[33]; + 34: out = in[34]; + 35: out = in[35]; + 36: out = in[36]; + 37: out = in[37]; + 38: out = in[38]; + 39: out = in[39]; + 40: out = in[40]; + 41: out = in[41]; + 42: out = in[42]; + 43: out = in[43]; + 44: out = in[44]; + 45: out = in[45]; + 46: out = in[46]; + 47: out = in[47]; + 48: out = in[48]; + 49: out = in[49]; + 50: out = in[50]; + 51: out = in[51]; + 52: out = in[52]; + 53: out = in[53]; + 54: out = in[54]; + 55: out = in[55]; + 56: out = in[56]; + 57: out = in[57]; + 58: out = in[58]; + 59: out = in[59]; + 60: out = in[60]; + 61: out = in[61]; + 62: out = in[62]; + 63: out = in[63]; + endcase +endmodule + diff --git a/tests/hana/test_simulation_mux_8_test.v b/tests/hana/test_simulation_mux_8_test.v new file mode 100644 index 000000000..f53a2c570 --- /dev/null +++ b/tests/hana/test_simulation_mux_8_test.v @@ -0,0 +1,14 @@ +module test(input [7:0] in, input [2:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + endcase +endmodule diff --git a/tests/hana/test_simulation_nand_1_test.v b/tests/hana/test_simulation_nand_1_test.v new file mode 100644 index 000000000..d8f34ee1f --- /dev/null +++ b/tests/hana/test_simulation_nand_1_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = ~(in[0] & in[1]); +endmodule diff --git a/tests/hana/test_simulation_nand_3_test.v b/tests/hana/test_simulation_nand_3_test.v new file mode 100644 index 000000000..8926cebb9 --- /dev/null +++ b/tests/hana/test_simulation_nand_3_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = !(in[0] & in[1] & in[2]); +endmodule diff --git a/tests/hana/test_simulation_nand_4_test.v b/tests/hana/test_simulation_nand_4_test.v new file mode 100644 index 000000000..703a2de45 --- /dev/null +++ b/tests/hana/test_simulation_nand_4_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = ~(in[0] && in[1] && in[2]); +endmodule diff --git a/tests/hana/test_simulation_nand_5_test.v b/tests/hana/test_simulation_nand_5_test.v new file mode 100644 index 000000000..adef3c903 --- /dev/null +++ b/tests/hana/test_simulation_nand_5_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = !(in[0] & in[1] & in[2] & in[3]); +endmodule diff --git a/tests/hana/test_simulation_nand_6_test.v b/tests/hana/test_simulation_nand_6_test.v new file mode 100644 index 000000000..a2136f211 --- /dev/null +++ b/tests/hana/test_simulation_nand_6_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = !(in[0] && in[1] && in[2] && in[3]); +endmodule diff --git a/tests/hana/test_simulation_nor_1_test.v b/tests/hana/test_simulation_nor_1_test.v new file mode 100644 index 000000000..df4e8bfaa --- /dev/null +++ b/tests/hana/test_simulation_nor_1_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = ~(in[0] | in[1]); +endmodule diff --git a/tests/hana/test_simulation_nor_2_test.v b/tests/hana/test_simulation_nor_2_test.v new file mode 100644 index 000000000..2cfffc45f --- /dev/null +++ b/tests/hana/test_simulation_nor_2_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = ~(in[0] | in[1] | in[2]); +endmodule diff --git a/tests/hana/test_simulation_nor_3_test.v b/tests/hana/test_simulation_nor_3_test.v new file mode 100644 index 000000000..9f1ef8fec --- /dev/null +++ b/tests/hana/test_simulation_nor_3_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = ~(in[0] | in[1] | in[2] | in[3]); +endmodule diff --git a/tests/hana/test_simulation_nor_4_test.v b/tests/hana/test_simulation_nor_4_test.v new file mode 100644 index 000000000..d8e685049 --- /dev/null +++ b/tests/hana/test_simulation_nor_4_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +nor mynor(out, in[0], in[1], in[2], in[3]); +endmodule diff --git a/tests/hana/test_simulation_opt_constprop_contassign_1_test.v b/tests/hana/test_simulation_opt_constprop_contassign_1_test.v new file mode 100644 index 000000000..a39b58b42 --- /dev/null +++ b/tests/hana/test_simulation_opt_constprop_contassign_1_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = 1'b1; +endmodule diff --git a/tests/hana/test_simulation_or_1_test.v b/tests/hana/test_simulation_or_1_test.v new file mode 100644 index 000000000..bdfffd3d7 --- /dev/null +++ b/tests/hana/test_simulation_or_1_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = in[0] | in[1]; +endmodule diff --git a/tests/hana/test_simulation_or_2_test.v b/tests/hana/test_simulation_or_2_test.v new file mode 100644 index 000000000..291c8c765 --- /dev/null +++ b/tests/hana/test_simulation_or_2_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = in[0] || in[1]; +endmodule diff --git a/tests/hana/test_simulation_or_3_test.v b/tests/hana/test_simulation_or_3_test.v new file mode 100644 index 000000000..ad00c7084 --- /dev/null +++ b/tests/hana/test_simulation_or_3_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = in[0] | in[1] | in[2]; +endmodule diff --git a/tests/hana/test_simulation_or_4_test.v b/tests/hana/test_simulation_or_4_test.v new file mode 100644 index 000000000..2ec57fa93 --- /dev/null +++ b/tests/hana/test_simulation_or_4_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = in[0] || in[1] || in[2]; +endmodule diff --git a/tests/hana/test_simulation_or_5_test.v b/tests/hana/test_simulation_or_5_test.v new file mode 100644 index 000000000..f6a2d14d4 --- /dev/null +++ b/tests/hana/test_simulation_or_5_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = in[0] | in[1] | in[2] | in[3]; +endmodule diff --git a/tests/hana/test_simulation_or_6_test.v b/tests/hana/test_simulation_or_6_test.v new file mode 100644 index 000000000..ecd85c363 --- /dev/null +++ b/tests/hana/test_simulation_or_6_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = in[0] || in[1] || in[2] || in[3]; +endmodule diff --git a/tests/hana/test_simulation_seq_ff_1_test.v b/tests/hana/test_simulation_seq_ff_1_test.v new file mode 100644 index 000000000..5aac49c03 --- /dev/null +++ b/tests/hana/test_simulation_seq_ff_1_test.v @@ -0,0 +1,4 @@ +module test(input in, input clk, output reg out); +always @(posedge clk) + out <= in; +endmodule diff --git a/tests/hana/test_simulation_seq_ff_2_test.v b/tests/hana/test_simulation_seq_ff_2_test.v new file mode 100644 index 000000000..f1d2b7b42 --- /dev/null +++ b/tests/hana/test_simulation_seq_ff_2_test.v @@ -0,0 +1,4 @@ +module test(input in, input clk, output reg out); +always @(negedge clk) + out <= in; +endmodule diff --git a/tests/hana/test_simulation_shifter_left_16_test.v b/tests/hana/test_simulation_shifter_left_16_test.v new file mode 100644 index 000000000..a57dac499 --- /dev/null +++ b/tests/hana/test_simulation_shifter_left_16_test.v @@ -0,0 +1,4 @@ +module test(input [15:0] IN, input [4:0] SHIFT, output [15:0] OUT); + +assign OUT = IN << SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_left_32_test.v b/tests/hana/test_simulation_shifter_left_32_test.v new file mode 100644 index 000000000..672938ace --- /dev/null +++ b/tests/hana/test_simulation_shifter_left_32_test.v @@ -0,0 +1,4 @@ +module test(input [31:0] IN, input [5:0] SHIFT, output [31:0] OUT); + +assign OUT = IN << SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_left_4_test.v b/tests/hana/test_simulation_shifter_left_4_test.v new file mode 100644 index 000000000..c525401f1 --- /dev/null +++ b/tests/hana/test_simulation_shifter_left_4_test.v @@ -0,0 +1,4 @@ +module test(input [3:0] IN, input [2:0] SHIFT, output [3:0] OUT); + +assign OUT = IN << SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_left_64_test.v b/tests/hana/test_simulation_shifter_left_64_test.v new file mode 100644 index 000000000..276a7c5a8 --- /dev/null +++ b/tests/hana/test_simulation_shifter_left_64_test.v @@ -0,0 +1,4 @@ +module test(input [63:0] IN, input [6:0] SHIFT, output [63:0] OUT); + +assign OUT = IN << SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_left_8_test.v b/tests/hana/test_simulation_shifter_left_8_test.v new file mode 100644 index 000000000..c17277001 --- /dev/null +++ b/tests/hana/test_simulation_shifter_left_8_test.v @@ -0,0 +1,4 @@ +module test(input [7:0] IN, input [3:0] SHIFT, output [7:0] OUT); + +assign OUT = IN << SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_right_16_test.v b/tests/hana/test_simulation_shifter_right_16_test.v new file mode 100644 index 000000000..6152adc06 --- /dev/null +++ b/tests/hana/test_simulation_shifter_right_16_test.v @@ -0,0 +1,4 @@ +module test(input [15:0] IN, input [4:0] SHIFT, output [15:0] OUT); + +assign OUT = IN >> SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_right_32_test.v b/tests/hana/test_simulation_shifter_right_32_test.v new file mode 100644 index 000000000..e910cdd6d --- /dev/null +++ b/tests/hana/test_simulation_shifter_right_32_test.v @@ -0,0 +1,4 @@ +module test(input [31:0] IN, input [5:0] SHIFT, output [31:0] OUT); + +assign OUT = IN >> SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_right_4_test.v b/tests/hana/test_simulation_shifter_right_4_test.v new file mode 100644 index 000000000..608c196de --- /dev/null +++ b/tests/hana/test_simulation_shifter_right_4_test.v @@ -0,0 +1,4 @@ +module test(input [3:0] IN, input [2:0] SHIFT, output [3:0] OUT); + +assign OUT = IN >> SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_right_64_test.v b/tests/hana/test_simulation_shifter_right_64_test.v new file mode 100644 index 000000000..c26d5938e --- /dev/null +++ b/tests/hana/test_simulation_shifter_right_64_test.v @@ -0,0 +1,4 @@ +module test(input [63:0] IN, input [6:0] SHIFT, output [63:0] OUT); + +assign OUT = IN >> SHIFT; +endmodule diff --git a/tests/hana/test_simulation_shifter_right_8_test.v b/tests/hana/test_simulation_shifter_right_8_test.v new file mode 100644 index 000000000..a91c594e5 --- /dev/null +++ b/tests/hana/test_simulation_shifter_right_8_test.v @@ -0,0 +1,4 @@ +module test(input [7:0] IN, input [3:0] SHIFT, output [7:0] OUT); + +assign OUT = IN >> SHIFT; +endmodule diff --git a/tests/hana/test_simulation_sop_basic_10_test.v b/tests/hana/test_simulation_sop_basic_10_test.v new file mode 100644 index 000000000..bc676c70b --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_10_test.v @@ -0,0 +1,8 @@ +module test(input [1:0] in, input select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + endcase +endmodule diff --git a/tests/hana/test_simulation_sop_basic_11_test.v b/tests/hana/test_simulation_sop_basic_11_test.v new file mode 100644 index 000000000..6a112c6a9 --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_11_test.v @@ -0,0 +1,10 @@ +module test(input [3:0] in, input [1:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + endcase +endmodule diff --git a/tests/hana/test_simulation_sop_basic_12_test.v b/tests/hana/test_simulation_sop_basic_12_test.v new file mode 100644 index 000000000..f53a2c570 --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_12_test.v @@ -0,0 +1,14 @@ +module test(input [7:0] in, input [2:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + endcase +endmodule diff --git a/tests/hana/test_simulation_sop_basic_18_test.v b/tests/hana/test_simulation_sop_basic_18_test.v new file mode 100644 index 000000000..03fc35b32 --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_18_test.v @@ -0,0 +1,5 @@ +module test(input [7:0] in, output out); + +assign out = ~^in; + +endmodule diff --git a/tests/hana/test_simulation_sop_basic_3_test.v b/tests/hana/test_simulation_sop_basic_3_test.v new file mode 100644 index 000000000..81759c25d --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_3_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = ~in; +endmodule diff --git a/tests/hana/test_simulation_sop_basic_7_test.v b/tests/hana/test_simulation_sop_basic_7_test.v new file mode 100644 index 000000000..e9bb7f617 --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_7_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = in; +endmodule diff --git a/tests/hana/test_simulation_sop_basic_8_test.v b/tests/hana/test_simulation_sop_basic_8_test.v new file mode 100644 index 000000000..a51ead0b2 --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_8_test.v @@ -0,0 +1,3 @@ +module test(output out); +assign out = 1'b0; +endmodule diff --git a/tests/hana/test_simulation_sop_basic_9_test.v b/tests/hana/test_simulation_sop_basic_9_test.v new file mode 100644 index 000000000..81759c25d --- /dev/null +++ b/tests/hana/test_simulation_sop_basic_9_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = ~in; +endmodule diff --git a/tests/hana/test_simulation_techmap_and_19_tech.v b/tests/hana/test_simulation_techmap_and_19_tech.v new file mode 100644 index 000000000..2491087cd --- /dev/null +++ b/tests/hana/test_simulation_techmap_and_19_tech.v @@ -0,0 +1,7 @@ +module TECH_AND18(input [17:0] in, output out); +assign out = ∈ +endmodule + +module TECH_AND4(input [3:0] in, output out); +assign out = ∈ +endmodule diff --git a/tests/hana/test_simulation_techmap_and_5_tech.v b/tests/hana/test_simulation_techmap_and_5_tech.v new file mode 100644 index 000000000..6ec6a61c4 --- /dev/null +++ b/tests/hana/test_simulation_techmap_and_5_tech.v @@ -0,0 +1,3 @@ +module TECH_AND5(input [4:0] in, output out); +assign out = ∈ +endmodule diff --git a/tests/hana/test_simulation_techmap_buf_test.v b/tests/hana/test_simulation_techmap_buf_test.v new file mode 100644 index 000000000..e9bb7f617 --- /dev/null +++ b/tests/hana/test_simulation_techmap_buf_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = in; +endmodule diff --git a/tests/hana/test_simulation_techmap_inv_test.v b/tests/hana/test_simulation_techmap_inv_test.v new file mode 100644 index 000000000..81759c25d --- /dev/null +++ b/tests/hana/test_simulation_techmap_inv_test.v @@ -0,0 +1,3 @@ +module test(input in, output out); +assign out = ~in; +endmodule diff --git a/tests/hana/test_simulation_techmap_mux_0_test.v b/tests/hana/test_simulation_techmap_mux_0_test.v new file mode 100644 index 000000000..bc676c70b --- /dev/null +++ b/tests/hana/test_simulation_techmap_mux_0_test.v @@ -0,0 +1,8 @@ +module test(input [1:0] in, input select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + endcase +endmodule diff --git a/tests/hana/test_simulation_techmap_mux_128_test.v b/tests/hana/test_simulation_techmap_mux_128_test.v new file mode 100644 index 000000000..544c016a8 --- /dev/null +++ b/tests/hana/test_simulation_techmap_mux_128_test.v @@ -0,0 +1,134 @@ +module test(input [127:0] in, input [6:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + 8: out = in[8]; + 9: out = in[9]; + 10: out = in[10]; + 11: out = in[11]; + 12: out = in[12]; + 13: out = in[13]; + 14: out = in[14]; + 15: out = in[15]; + 16: out = in[16]; + 17: out = in[17]; + 18: out = in[18]; + 19: out = in[19]; + 20: out = in[20]; + 21: out = in[21]; + 22: out = in[22]; + 23: out = in[23]; + 24: out = in[24]; + 25: out = in[25]; + 26: out = in[26]; + 27: out = in[27]; + 28: out = in[28]; + 29: out = in[29]; + 30: out = in[30]; + 31: out = in[31]; + 32: out = in[32]; + 33: out = in[33]; + 34: out = in[34]; + 35: out = in[35]; + 36: out = in[36]; + 37: out = in[37]; + 38: out = in[38]; + 39: out = in[39]; + 40: out = in[40]; + 41: out = in[41]; + 42: out = in[42]; + 43: out = in[43]; + 44: out = in[44]; + 45: out = in[45]; + 46: out = in[46]; + 47: out = in[47]; + 48: out = in[48]; + 49: out = in[49]; + 50: out = in[50]; + 51: out = in[51]; + 52: out = in[52]; + 53: out = in[53]; + 54: out = in[54]; + 55: out = in[55]; + 56: out = in[56]; + 57: out = in[57]; + 58: out = in[58]; + 59: out = in[59]; + 60: out = in[60]; + 61: out = in[61]; + 62: out = in[62]; + 63: out = in[63]; + 64: out = in[64]; + 65: out = in[65]; + 66: out = in[66]; + 67: out = in[67]; + 68: out = in[68]; + 69: out = in[69]; + 70: out = in[70]; + 71: out = in[71]; + 72: out = in[72]; + 73: out = in[73]; + 74: out = in[74]; + 75: out = in[75]; + 76: out = in[76]; + 77: out = in[77]; + 78: out = in[78]; + 79: out = in[79]; + 80: out = in[80]; + 81: out = in[81]; + 82: out = in[82]; + 83: out = in[83]; + 84: out = in[84]; + 85: out = in[85]; + 86: out = in[86]; + 87: out = in[87]; + 88: out = in[88]; + 89: out = in[89]; + 90: out = in[90]; + 91: out = in[91]; + 92: out = in[92]; + 93: out = in[93]; + 94: out = in[94]; + 95: out = in[95]; + 96: out = in[96]; + 97: out = in[97]; + 98: out = in[98]; + 99: out = in[99]; + 100: out = in[100]; + 101: out = in[101]; + 102: out = in[102]; + 103: out = in[103]; + 104: out = in[104]; + 105: out = in[105]; + 106: out = in[106]; + 107: out = in[107]; + 108: out = in[108]; + 109: out = in[109]; + 110: out = in[110]; + 111: out = in[111]; + 112: out = in[112]; + 113: out = in[113]; + 114: out = in[114]; + 115: out = in[115]; + 116: out = in[116]; + 117: out = in[117]; + 118: out = in[118]; + 119: out = in[119]; + 120: out = in[120]; + 121: out = in[121]; + 122: out = in[122]; + 123: out = in[123]; + 124: out = in[124]; + 125: out = in[125]; + 126: out = in[126]; + 127: out = in[127]; + endcase +endmodule diff --git a/tests/hana/test_simulation_techmap_mux_8_test.v b/tests/hana/test_simulation_techmap_mux_8_test.v new file mode 100644 index 000000000..f53a2c570 --- /dev/null +++ b/tests/hana/test_simulation_techmap_mux_8_test.v @@ -0,0 +1,14 @@ +module test(input [7:0] in, input [2:0] select, output reg out); + +always @( in or select) + case (select) + 0: out = in[0]; + 1: out = in[1]; + 2: out = in[2]; + 3: out = in[3]; + 4: out = in[4]; + 5: out = in[5]; + 6: out = in[6]; + 7: out = in[7]; + endcase +endmodule diff --git a/tests/hana/test_simulation_techmap_nand_19_tech.v b/tests/hana/test_simulation_techmap_nand_19_tech.v new file mode 100644 index 000000000..6a119e1ee --- /dev/null +++ b/tests/hana/test_simulation_techmap_nand_19_tech.v @@ -0,0 +1,11 @@ +module TECH_NAND18(input [17:0] in, output out); +assign out = ~(&in); +endmodule + +module TECH_NAND4(input [3:0] in, output out); +assign out = ~(&in); +endmodule + +module TECH_NAND2(input [1:0] in, output out); +assign out = ~(&in); +endmodule diff --git a/tests/hana/test_simulation_techmap_nand_2_tech.v b/tests/hana/test_simulation_techmap_nand_2_tech.v new file mode 100644 index 000000000..6a119e1ee --- /dev/null +++ b/tests/hana/test_simulation_techmap_nand_2_tech.v @@ -0,0 +1,11 @@ +module TECH_NAND18(input [17:0] in, output out); +assign out = ~(&in); +endmodule + +module TECH_NAND4(input [3:0] in, output out); +assign out = ~(&in); +endmodule + +module TECH_NAND2(input [1:0] in, output out); +assign out = ~(&in); +endmodule diff --git a/tests/hana/test_simulation_techmap_nand_5_tech.v b/tests/hana/test_simulation_techmap_nand_5_tech.v new file mode 100644 index 000000000..6a119e1ee --- /dev/null +++ b/tests/hana/test_simulation_techmap_nand_5_tech.v @@ -0,0 +1,11 @@ +module TECH_NAND18(input [17:0] in, output out); +assign out = ~(&in); +endmodule + +module TECH_NAND4(input [3:0] in, output out); +assign out = ~(&in); +endmodule + +module TECH_NAND2(input [1:0] in, output out); +assign out = ~(&in); +endmodule diff --git a/tests/hana/test_simulation_techmap_nor_19_tech.v b/tests/hana/test_simulation_techmap_nor_19_tech.v new file mode 100644 index 000000000..89fb2c7e8 --- /dev/null +++ b/tests/hana/test_simulation_techmap_nor_19_tech.v @@ -0,0 +1,11 @@ +module TECH_NOR18(input [17:0] in, output out); +assign out = ~(|in); +endmodule + +module TECH_NOR4(input [3:0] in, output out); +assign out = ~(|in); +endmodule + +module TECH_NOR2(input [1:0] in, output out); +assign out = ~(|in); +endmodule diff --git a/tests/hana/test_simulation_techmap_nor_2_tech.v b/tests/hana/test_simulation_techmap_nor_2_tech.v new file mode 100644 index 000000000..89fb2c7e8 --- /dev/null +++ b/tests/hana/test_simulation_techmap_nor_2_tech.v @@ -0,0 +1,11 @@ +module TECH_NOR18(input [17:0] in, output out); +assign out = ~(|in); +endmodule + +module TECH_NOR4(input [3:0] in, output out); +assign out = ~(|in); +endmodule + +module TECH_NOR2(input [1:0] in, output out); +assign out = ~(|in); +endmodule diff --git a/tests/hana/test_simulation_techmap_nor_5_tech.v b/tests/hana/test_simulation_techmap_nor_5_tech.v new file mode 100644 index 000000000..89fb2c7e8 --- /dev/null +++ b/tests/hana/test_simulation_techmap_nor_5_tech.v @@ -0,0 +1,11 @@ +module TECH_NOR18(input [17:0] in, output out); +assign out = ~(|in); +endmodule + +module TECH_NOR4(input [3:0] in, output out); +assign out = ~(|in); +endmodule + +module TECH_NOR2(input [1:0] in, output out); +assign out = ~(|in); +endmodule diff --git a/tests/hana/test_simulation_techmap_or_19_tech.v b/tests/hana/test_simulation_techmap_or_19_tech.v new file mode 100644 index 000000000..745d7b71c --- /dev/null +++ b/tests/hana/test_simulation_techmap_or_19_tech.v @@ -0,0 +1,7 @@ +module TECH_OR18(input [17:0] in, output out); +assign out = |in; +endmodule + +module TECH_OR4(input [3:0] in, output out); +assign out = |in; +endmodule diff --git a/tests/hana/test_simulation_techmap_or_5_tech.v b/tests/hana/test_simulation_techmap_or_5_tech.v new file mode 100644 index 000000000..05c38b670 --- /dev/null +++ b/tests/hana/test_simulation_techmap_or_5_tech.v @@ -0,0 +1,3 @@ +module TECH_OR5(input [4:0] in, output out); +assign out = |in; +endmodule diff --git a/tests/hana/test_simulation_techmap_xnor_2_tech.v b/tests/hana/test_simulation_techmap_xnor_2_tech.v new file mode 100644 index 000000000..4eb05683f --- /dev/null +++ b/tests/hana/test_simulation_techmap_xnor_2_tech.v @@ -0,0 +1,6 @@ +module TECH_XOR5(input [4:0] in, output out); +assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4]; +endmodule +module TECH_XOR2(input [1:0] in, output out); +assign out = in[0] ^ in[1]; +endmodule diff --git a/tests/hana/test_simulation_techmap_xnor_5_tech.v b/tests/hana/test_simulation_techmap_xnor_5_tech.v new file mode 100644 index 000000000..4eb05683f --- /dev/null +++ b/tests/hana/test_simulation_techmap_xnor_5_tech.v @@ -0,0 +1,6 @@ +module TECH_XOR5(input [4:0] in, output out); +assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4]; +endmodule +module TECH_XOR2(input [1:0] in, output out); +assign out = in[0] ^ in[1]; +endmodule diff --git a/tests/hana/test_simulation_techmap_xor_19_tech.v b/tests/hana/test_simulation_techmap_xor_19_tech.v new file mode 100644 index 000000000..2042a0ad0 --- /dev/null +++ b/tests/hana/test_simulation_techmap_xor_19_tech.v @@ -0,0 +1,3 @@ +module TECH_XOR2(input [1:0] in, output out); +assign out = in[0] ^ in[1]; +endmodule diff --git a/tests/hana/test_simulation_techmap_xor_2_tech.v b/tests/hana/test_simulation_techmap_xor_2_tech.v new file mode 100644 index 000000000..4eb05683f --- /dev/null +++ b/tests/hana/test_simulation_techmap_xor_2_tech.v @@ -0,0 +1,6 @@ +module TECH_XOR5(input [4:0] in, output out); +assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4]; +endmodule +module TECH_XOR2(input [1:0] in, output out); +assign out = in[0] ^ in[1]; +endmodule diff --git a/tests/hana/test_simulation_techmap_xor_5_tech.v b/tests/hana/test_simulation_techmap_xor_5_tech.v new file mode 100644 index 000000000..4eb05683f --- /dev/null +++ b/tests/hana/test_simulation_techmap_xor_5_tech.v @@ -0,0 +1,6 @@ +module TECH_XOR5(input [4:0] in, output out); +assign out = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4]; +endmodule +module TECH_XOR2(input [1:0] in, output out); +assign out = in[0] ^ in[1]; +endmodule diff --git a/tests/hana/test_simulation_tribuf_2_test.v b/tests/hana/test_simulation_tribuf_2_test.v new file mode 100644 index 000000000..1e82aaf04 --- /dev/null +++ b/tests/hana/test_simulation_tribuf_2_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, input enable, output [1:0] out); +assign out = enable ? in : 2'bzz; +endmodule diff --git a/tests/hana/test_simulation_xnor_1_test.v b/tests/hana/test_simulation_xnor_1_test.v new file mode 100644 index 000000000..adc6ae5ca --- /dev/null +++ b/tests/hana/test_simulation_xnor_1_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = ~(in[0] ^ in[1]); +endmodule diff --git a/tests/hana/test_simulation_xnor_2_test.v b/tests/hana/test_simulation_xnor_2_test.v new file mode 100644 index 000000000..701bcc775 --- /dev/null +++ b/tests/hana/test_simulation_xnor_2_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = ~(in[0] ^ in[1] ^ in[2]); +endmodule diff --git a/tests/hana/test_simulation_xnor_3_test.v b/tests/hana/test_simulation_xnor_3_test.v new file mode 100644 index 000000000..a8c87cc62 --- /dev/null +++ b/tests/hana/test_simulation_xnor_3_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = ~(in[0] ^ in[1] ^ in[2] ^ in[3]); +endmodule diff --git a/tests/hana/test_simulation_xnor_4_test.v b/tests/hana/test_simulation_xnor_4_test.v new file mode 100644 index 000000000..fa671ff93 --- /dev/null +++ b/tests/hana/test_simulation_xnor_4_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +xnor myxnor(out, in[0], in[1], in[2], in[3]); +endmodule diff --git a/tests/hana/test_simulation_xor_1_test.v b/tests/hana/test_simulation_xor_1_test.v new file mode 100644 index 000000000..f6447f817 --- /dev/null +++ b/tests/hana/test_simulation_xor_1_test.v @@ -0,0 +1,3 @@ +module test(input [1:0] in, output out); +assign out = (in[0] ^ in[1]); +endmodule diff --git a/tests/hana/test_simulation_xor_2_test.v b/tests/hana/test_simulation_xor_2_test.v new file mode 100644 index 000000000..d94081df7 --- /dev/null +++ b/tests/hana/test_simulation_xor_2_test.v @@ -0,0 +1,3 @@ +module test(input [2:0] in, output out); +assign out = (in[0] ^ in[1] ^ in[2]); +endmodule diff --git a/tests/hana/test_simulation_xor_3_test.v b/tests/hana/test_simulation_xor_3_test.v new file mode 100644 index 000000000..cfa13187f --- /dev/null +++ b/tests/hana/test_simulation_xor_3_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +assign out = (in[0] ^ in[1] ^ in[2] ^ in[3]); +endmodule diff --git a/tests/hana/test_simulation_xor_4_test.v b/tests/hana/test_simulation_xor_4_test.v new file mode 100644 index 000000000..be6cab633 --- /dev/null +++ b/tests/hana/test_simulation_xor_4_test.v @@ -0,0 +1,3 @@ +module test(input [3:0] in, output out); +xor myxor(out, in[0], in[1], in[2], in[3]); +endmodule diff --git a/tests/i2c_bench/i2c_master_bit_ctrl.v b/tests/i2c_bench/i2c_master_bit_ctrl.v new file mode 100644 index 000000000..6594fd60c --- /dev/null +++ b/tests/i2c_bench/i2c_master_bit_ctrl.v @@ -0,0 +1,576 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master bit-controller //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_bit_ctrl.v,v 1.14 2009-01-20 10:25:29 rherveille Exp $ +// +// $Date: 2009-01-20 10:25:29 $ +// $Revision: 1.14 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: $ +// Revision 1.14 2009/01/20 10:25:29 rherveille +// Added clock synchronization logic +// Fixed slave_wait signal +// +// Revision 1.13 2009/01/19 20:29:26 rherveille +// Fixed synopsys miss spell (synopsis) +// Fixed cr[0] register width +// Fixed ! usage instead of ~ +// Fixed bit controller parameter width to 18bits +// +// Revision 1.12 2006/09/04 09:08:13 rherveille +// fixed short scl high pulse after clock stretch +// fixed slave model not returning correct '(n)ack' signal +// +// Revision 1.11 2004/05/07 11:02:26 rherveille +// Fixed a bug where the core would signal an arbitration lost (AL bit set), when another master controls the bus and the other master generates a STOP bit. +// +// Revision 1.10 2003/08/09 07:01:33 rherveille +// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. +// Fixed a potential bug in the byte controller's host-acknowledge generation. +// +// Revision 1.9 2003/03/10 14:26:37 rherveille +// Fixed cmd_ack generation item (no bug). +// +// Revision 1.8 2003/02/05 00:06:10 rherveille +// Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles. +// +// Revision 1.7 2002/12/26 16:05:12 rherveille +// Small code simplifications +// +// Revision 1.6 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.5 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.4 2002/10/30 18:10:07 rherveille +// Fixed some reported minor start/stop generation timing issuess. +// +// Revision 1.3 2002/06/15 07:37:03 rherveille +// Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment. +// +// Revision 1.2 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + +// +///////////////////////////////////// +// Bit controller section +///////////////////////////////////// +// +// Translate simple commands into SCL/SDA transitions +// Each command has 5 states, A/B/C/D/idle +// +// start: SCL ~~~~~~~~~~\____ +// SDA ~~~~~~~~\______ +// x | A | B | C | D | i +// +// repstart SCL ____/~~~~\___ +// SDA __/~~~\______ +// x | A | B | C | D | i +// +// stop SCL ____/~~~~~~~~ +// SDA ==\____/~~~~~ +// x | A | B | C | D | i +// +//- write SCL ____/~~~~\____ +// SDA ==X=========X= +// x | A | B | C | D | i +// +//- read SCL ____/~~~~\____ +// SDA XXXX=====XXXX +// x | A | B | C | D | i +// + +// Timing: Normal mode Fast mode +/////////////////////////////////////////////////////////////////////// +// Fscl 100KHz 400KHz +// Th_scl 4.0us 0.6us High period of SCL +// Tl_scl 4.7us 1.3us Low period of SCL +// Tsu:sta 4.7us 0.6us setup time for a repeated start condition +// Tsu:sto 4.0us 0.6us setup time for a stop conditon +// Tbuf 4.7us 1.3us Bus free time between a stop and start condition +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +`include "i2c_master_defines.v" + +module i2c_master_bit_ctrl ( + input clk, // system clock + input rst, // synchronous active high reset + input nReset, // asynchronous active low reset + input ena, // core enable signal + + input [15:0] clk_cnt, // clock prescale value + + input [ 3:0] cmd, // command (from byte controller) + output reg cmd_ack, // command complete acknowledge + output reg busy, // i2c bus busy + output reg al, // i2c bus arbitration lost + + input din, + output reg dout, + + input scl_i, // i2c clock line input + output scl_o, // i2c clock line output + output reg scl_oen, // i2c clock line output enable (active low) + input sda_i, // i2c data line input + output sda_o, // i2c data line output + output reg sda_oen // i2c data line output enable (active low) +); + + + // + // variable declarations + // + + reg [ 1:0] cSCL, cSDA; // capture SCL and SDA + reg [ 2:0] fSCL, fSDA; // SCL and SDA filter inputs + reg sSCL, sSDA; // filtered and synchronized SCL and SDA inputs + reg dSCL, dSDA; // delayed versions of sSCL and sSDA + reg dscl_oen; // delayed scl_oen + reg sda_chk; // check SDA output (Multi-master arbitration) + reg clk_en; // clock generation signals + reg slave_wait; // slave inserts wait states + reg [15:0] cnt; // clock divider counter (synthesis) + reg [13:0] filter_cnt; // clock divider for filter + + + // state machine variable + reg [17:0] c_state; // synopsys enum_state + + // + // module body + // + + // whenever the slave is not ready it can delay the cycle by pulling SCL low + // delay scl_oen + always @(posedge clk) + dscl_oen <= scl_oen; + + // slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low + // slave_wait remains asserted until the slave releases SCL + always @(posedge clk or negedge nReset) + if (!nReset) slave_wait <= 1'b0; + else slave_wait <= (scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL); + + // master drives SCL high, but another master pulls it low + // master start counting down its low cycle now (clock synchronization) + wire scl_sync = dSCL & ~sSCL & scl_oen; + + + // generate clk enable signal + always @(posedge clk or negedge nReset) + if (~nReset) + begin + cnt <= 16'h0; + clk_en <= 1'b1; + end + else if (rst || ~|cnt || !ena || scl_sync) + begin + cnt <= clk_cnt; + clk_en <= 1'b1; + end + else if (slave_wait) + begin + cnt <= cnt; + clk_en <= 1'b0; + end + else + begin + cnt <= cnt - 16'h1; + clk_en <= 1'b0; + end + + + // generate bus status controller + + // capture SDA and SCL + // reduce metastability risk + always @(posedge clk or negedge nReset) + if (!nReset) + begin + cSCL <= 2'b00; + cSDA <= 2'b00; + end + else if (rst) + begin + cSCL <= 2'b00; + cSDA <= 2'b00; + end + else + begin + cSCL <= {cSCL[0],scl_i}; + cSDA <= {cSDA[0],sda_i}; + end + + + // filter SCL and SDA signals; (attempt to) remove glitches + always @(posedge clk or negedge nReset) + if (!nReset ) filter_cnt <= 14'h0; + else if (rst || !ena ) filter_cnt <= 14'h0; + else if (~|filter_cnt) filter_cnt <= clk_cnt[15:2]; //16x I2C bus frequency + else filter_cnt <= filter_cnt -1; + + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + fSCL <= 3'b111; + fSDA <= 3'b111; + end + else if (rst) + begin + fSCL <= 3'b111; + fSDA <= 3'b111; + end + else if (~|filter_cnt) + begin + fSCL <= {fSCL[1:0],cSCL[1]}; + fSDA <= {fSDA[1:0],cSDA[1]}; + end + + + // generate filtered SCL and SDA signals + always @(posedge clk or negedge nReset) + if (~nReset) + begin + sSCL <= 1'b1; + sSDA <= 1'b1; + + dSCL <= 1'b1; + dSDA <= 1'b1; + end + else if (rst) + begin + sSCL <= 1'b1; + sSDA <= 1'b1; + + dSCL <= 1'b1; + dSDA <= 1'b1; + end + else + begin + sSCL <= &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]); + sSDA <= &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]); + + dSCL <= sSCL; + dSDA <= sSDA; + end + + // detect start condition => detect falling edge on SDA while SCL is high + // detect stop condition => detect rising edge on SDA while SCL is high + reg sta_condition; + reg sto_condition; + always @(posedge clk or negedge nReset) + if (~nReset) + begin + sta_condition <= 1'b0; + sto_condition <= 1'b0; + end + else if (rst) + begin + sta_condition <= 1'b0; + sto_condition <= 1'b0; + end + else + begin + sta_condition <= ~sSDA & dSDA & sSCL; + sto_condition <= sSDA & ~dSDA & sSCL; + end + + + // generate i2c bus busy signal + always @(posedge clk or negedge nReset) + if (!nReset) busy <= 1'b0; + else if (rst ) busy <= 1'b0; + else busy <= (sta_condition | busy) & ~sto_condition; + + + // generate arbitration lost signal + // aribitration lost when: + // 1) master drives SDA high, but the i2c bus is low + // 2) stop detected while not requested + reg cmd_stop; + always @(posedge clk or negedge nReset) + if (~nReset) + cmd_stop <= 1'b0; + else if (rst) + cmd_stop <= 1'b0; + else if (clk_en) + cmd_stop <= cmd == `I2C_CMD_STOP; + + always @(posedge clk or negedge nReset) + if (~nReset) + al <= 1'b0; + else if (rst) + al <= 1'b0; + else + al <= (sda_chk & ~sSDA & sda_oen) | (|c_state & sto_condition & ~cmd_stop); + + + // generate dout signal (store SDA on rising edge of SCL) + always @(posedge clk) + if (sSCL & ~dSCL) dout <= sSDA; + + + // generate statemachine + + // nxt_state decoder + parameter [17:0] idle = 18'b0_0000_0000_0000_0000; + parameter [17:0] start_a = 18'b0_0000_0000_0000_0001; + parameter [17:0] start_b = 18'b0_0000_0000_0000_0010; + parameter [17:0] start_c = 18'b0_0000_0000_0000_0100; + parameter [17:0] start_d = 18'b0_0000_0000_0000_1000; + parameter [17:0] start_e = 18'b0_0000_0000_0001_0000; + parameter [17:0] stop_a = 18'b0_0000_0000_0010_0000; + parameter [17:0] stop_b = 18'b0_0000_0000_0100_0000; + parameter [17:0] stop_c = 18'b0_0000_0000_1000_0000; + parameter [17:0] stop_d = 18'b0_0000_0001_0000_0000; + parameter [17:0] rd_a = 18'b0_0000_0010_0000_0000; + parameter [17:0] rd_b = 18'b0_0000_0100_0000_0000; + parameter [17:0] rd_c = 18'b0_0000_1000_0000_0000; + parameter [17:0] rd_d = 18'b0_0001_0000_0000_0000; + parameter [17:0] wr_a = 18'b0_0010_0000_0000_0000; + parameter [17:0] wr_b = 18'b0_0100_0000_0000_0000; + parameter [17:0] wr_c = 18'b0_1000_0000_0000_0000; + parameter [17:0] wr_d = 18'b1_0000_0000_0000_0000; + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + c_state <= idle; + cmd_ack <= 1'b0; + scl_oen <= 1'b1; + sda_oen <= 1'b1; + sda_chk <= 1'b0; + end + else if (rst | al) + begin + c_state <= idle; + cmd_ack <= 1'b0; + scl_oen <= 1'b1; + sda_oen <= 1'b1; + sda_chk <= 1'b0; + end + else + begin + cmd_ack <= 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle + + if (clk_en) + case (c_state) // synopsys full_case parallel_case + // idle state + idle: + begin + case (cmd) // synopsys full_case parallel_case + `I2C_CMD_START: c_state <= start_a; + `I2C_CMD_STOP: c_state <= stop_a; + `I2C_CMD_WRITE: c_state <= wr_a; + `I2C_CMD_READ: c_state <= rd_a; + default: c_state <= idle; + endcase + + scl_oen <= scl_oen; // keep SCL in same state + sda_oen <= sda_oen; // keep SDA in same state + sda_chk <= 1'b0; // don't check SDA output + end + + // start + start_a: + begin + c_state <= start_b; + scl_oen <= scl_oen; // keep SCL in same state + sda_oen <= 1'b1; // set SDA high + sda_chk <= 1'b0; // don't check SDA output + end + + start_b: + begin + c_state <= start_c; + scl_oen <= 1'b1; // set SCL high + sda_oen <= 1'b1; // keep SDA high + sda_chk <= 1'b0; // don't check SDA output + end + + start_c: + begin + c_state <= start_d; + scl_oen <= 1'b1; // keep SCL high + sda_oen <= 1'b0; // set SDA low + sda_chk <= 1'b0; // don't check SDA output + end + + start_d: + begin + c_state <= start_e; + scl_oen <= 1'b1; // keep SCL high + sda_oen <= 1'b0; // keep SDA low + sda_chk <= 1'b0; // don't check SDA output + end + + start_e: + begin + c_state <= idle; + cmd_ack <= 1'b1; + scl_oen <= 1'b0; // set SCL low + sda_oen <= 1'b0; // keep SDA low + sda_chk <= 1'b0; // don't check SDA output + end + + // stop + stop_a: + begin + c_state <= stop_b; + scl_oen <= 1'b0; // keep SCL low + sda_oen <= 1'b0; // set SDA low + sda_chk <= 1'b0; // don't check SDA output + end + + stop_b: + begin + c_state <= stop_c; + scl_oen <= 1'b1; // set SCL high + sda_oen <= 1'b0; // keep SDA low + sda_chk <= 1'b0; // don't check SDA output + end + + stop_c: + begin + c_state <= stop_d; + scl_oen <= 1'b1; // keep SCL high + sda_oen <= 1'b0; // keep SDA low + sda_chk <= 1'b0; // don't check SDA output + end + + stop_d: + begin + c_state <= idle; + cmd_ack <= 1'b1; + scl_oen <= 1'b1; // keep SCL high + sda_oen <= 1'b1; // set SDA high + sda_chk <= 1'b0; // don't check SDA output + end + + // read + rd_a: + begin + c_state <= rd_b; + scl_oen <= 1'b0; // keep SCL low + sda_oen <= 1'b1; // tri-state SDA + sda_chk <= 1'b0; // don't check SDA output + end + + rd_b: + begin + c_state <= rd_c; + scl_oen <= 1'b1; // set SCL high + sda_oen <= 1'b1; // keep SDA tri-stated + sda_chk <= 1'b0; // don't check SDA output + end + + rd_c: + begin + c_state <= rd_d; + scl_oen <= 1'b1; // keep SCL high + sda_oen <= 1'b1; // keep SDA tri-stated + sda_chk <= 1'b0; // don't check SDA output + end + + rd_d: + begin + c_state <= idle; + cmd_ack <= 1'b1; + scl_oen <= 1'b0; // set SCL low + sda_oen <= 1'b1; // keep SDA tri-stated + sda_chk <= 1'b0; // don't check SDA output + end + + // write + wr_a: + begin + c_state <= wr_b; + scl_oen <= 1'b0; // keep SCL low + sda_oen <= din; // set SDA + sda_chk <= 1'b0; // don't check SDA output (SCL low) + end + + wr_b: + begin + c_state <= wr_c; + scl_oen <= 1'b1; // set SCL high + sda_oen <= din; // keep SDA + sda_chk <= 1'b0; // don't check SDA output yet + // allow some time for SDA and SCL to settle + end + + wr_c: + begin + c_state <= wr_d; + scl_oen <= 1'b1; // keep SCL high + sda_oen <= din; + sda_chk <= 1'b1; // check SDA output + end + + wr_d: + begin + c_state <= idle; + cmd_ack <= 1'b1; + scl_oen <= 1'b0; // set SCL low + sda_oen <= din; + sda_chk <= 1'b0; // don't check SDA output (SCL low) + end + + endcase + end + + + // assign scl and sda output (always gnd) + assign scl_o = 1'b0; + assign sda_o = 1'b0; + +endmodule diff --git a/tests/i2c_bench/i2c_master_byte_ctrl.v b/tests/i2c_bench/i2c_master_byte_ctrl.v new file mode 100644 index 000000000..513953a82 --- /dev/null +++ b/tests/i2c_bench/i2c_master_byte_ctrl.v @@ -0,0 +1,344 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master byte-controller //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_byte_ctrl.v,v 1.8 2009-01-19 20:29:26 rherveille Exp $ +// +// $Date: 2009-01-19 20:29:26 $ +// $Revision: 1.8 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.7 2004/02/18 11:40:46 rherveille +// Fixed a potential bug in the statemachine. During a 'stop' 2 cmd_ack signals were generated. Possibly canceling a new start command. +// +// Revision 1.6 2003/08/09 07:01:33 rherveille +// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. +// Fixed a potential bug in the byte controller's host-acknowledge generation. +// +// Revision 1.5 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.4 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.3 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +`include "i2c_master_defines.v" + +module i2c_master_byte_ctrl ( + clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din, + cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); + + // + // inputs & outputs + // + input clk; // master clock + input rst; // synchronous active high reset + input nReset; // asynchronous active low reset + input ena; // core enable signal + + input [15:0] clk_cnt; // 4x SCL + + // control inputs + input start; + input stop; + input read; + input write; + input ack_in; + input [7:0] din; + + // status outputs + output cmd_ack; + reg cmd_ack; + output ack_out; + reg ack_out; + output i2c_busy; + output i2c_al; + output [7:0] dout; + + // I2C signals + input scl_i; + output scl_o; + output scl_oen; + input sda_i; + output sda_o; + output sda_oen; + + + // + // Variable declarations + // + + // statemachine + parameter [4:0] ST_IDLE = 5'b0_0000; + parameter [4:0] ST_START = 5'b0_0001; + parameter [4:0] ST_READ = 5'b0_0010; + parameter [4:0] ST_WRITE = 5'b0_0100; + parameter [4:0] ST_ACK = 5'b0_1000; + parameter [4:0] ST_STOP = 5'b1_0000; + + // signals for bit_controller + reg [3:0] core_cmd; + reg core_txd; + wire core_ack, core_rxd; + + // signals for shift register + reg [7:0] sr; //8bit shift register + reg shift, ld; + + // signals for state machine + wire go; + reg [2:0] dcnt; + wire cnt_done; + + // + // Module body + // + + // hookup bit_controller + i2c_master_bit_ctrl bit_controller ( + .clk ( clk ), + .rst ( rst ), + .nReset ( nReset ), + .ena ( ena ), + .clk_cnt ( clk_cnt ), + .cmd ( core_cmd ), + .cmd_ack ( core_ack ), + .busy ( i2c_busy ), + .al ( i2c_al ), + .din ( core_txd ), + .dout ( core_rxd ), + .scl_i ( scl_i ), + .scl_o ( scl_o ), + .scl_oen ( scl_oen ), + .sda_i ( sda_i ), + .sda_o ( sda_o ), + .sda_oen ( sda_oen ) + ); + + // generate go-signal + assign go = (read | write | stop) & ~cmd_ack; + + // assign dout output to shift-register + assign dout = sr; + + // generate shift register + always @(posedge clk or negedge nReset) + if (!nReset) + sr <= 8'h0; + else if (rst) + sr <= 8'h0; + else if (ld) + sr <= din; + else if (shift) + sr <= {sr[6:0], core_rxd}; + + // generate counter + always @(posedge clk or negedge nReset) + if (!nReset) + dcnt <= 3'h0; + else if (rst) + dcnt <= 3'h0; + else if (ld) + dcnt <= 3'h7; + else if (shift) + dcnt <= dcnt - 3'h1; + + assign cnt_done = ~(|dcnt); + + // + // state machine + // + reg [4:0] c_state; // synopsys enum_state + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + core_cmd <= `I2C_CMD_NOP; + core_txd <= 1'b0; + shift <= 1'b0; + ld <= 1'b0; + cmd_ack <= 1'b0; + c_state <= ST_IDLE; + ack_out <= 1'b0; + end + else if (rst | i2c_al) + begin + core_cmd <= `I2C_CMD_NOP; + core_txd <= 1'b0; + shift <= 1'b0; + ld <= 1'b0; + cmd_ack <= 1'b0; + c_state <= ST_IDLE; + ack_out <= 1'b0; + end + else + begin + // initially reset all signals + core_txd <= sr[7]; + shift <= 1'b0; + ld <= 1'b0; + cmd_ack <= 1'b0; + + case (c_state) // synopsys full_case parallel_case + ST_IDLE: + if (go) + begin + if (start) + begin + c_state <= ST_START; + core_cmd <= `I2C_CMD_START; + end + else if (read) + begin + c_state <= ST_READ; + core_cmd <= `I2C_CMD_READ; + end + else if (write) + begin + c_state <= ST_WRITE; + core_cmd <= `I2C_CMD_WRITE; + end + else // stop + begin + c_state <= ST_STOP; + core_cmd <= `I2C_CMD_STOP; + end + + ld <= 1'b1; + end + + ST_START: + if (core_ack) + begin + if (read) + begin + c_state <= ST_READ; + core_cmd <= `I2C_CMD_READ; + end + else + begin + c_state <= ST_WRITE; + core_cmd <= `I2C_CMD_WRITE; + end + + ld <= 1'b1; + end + + ST_WRITE: + if (core_ack) + if (cnt_done) + begin + c_state <= ST_ACK; + core_cmd <= `I2C_CMD_READ; + end + else + begin + c_state <= ST_WRITE; // stay in same state + core_cmd <= `I2C_CMD_WRITE; // write next bit + shift <= 1'b1; + end + + ST_READ: + if (core_ack) + begin + if (cnt_done) + begin + c_state <= ST_ACK; + core_cmd <= `I2C_CMD_WRITE; + end + else + begin + c_state <= ST_READ; // stay in same state + core_cmd <= `I2C_CMD_READ; // read next bit + end + + shift <= 1'b1; + core_txd <= ack_in; + end + + ST_ACK: + if (core_ack) + begin + if (stop) + begin + c_state <= ST_STOP; + core_cmd <= `I2C_CMD_STOP; + end + else + begin + c_state <= ST_IDLE; + core_cmd <= `I2C_CMD_NOP; + + // generate command acknowledge signal + cmd_ack <= 1'b1; + end + + // assign ack_out output to bit_controller_rxd (contains last received bit) + ack_out <= core_rxd; + + core_txd <= 1'b1; + end + else + core_txd <= ack_in; + + ST_STOP: + if (core_ack) + begin + c_state <= ST_IDLE; + core_cmd <= `I2C_CMD_NOP; + + // generate command acknowledge signal + cmd_ack <= 1'b1; + end + + endcase + end +endmodule diff --git a/tests/i2c_bench/i2c_master_defines.v b/tests/i2c_bench/i2c_master_defines.v new file mode 100644 index 000000000..e81c546a6 --- /dev/null +++ b/tests/i2c_bench/i2c_master_defines.v @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master controller defines //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_defines.v,v 1.3 2001-11-05 11:59:25 rherveille Exp $ +// +// $Date: 2001-11-05 11:59:25 $ +// $Revision: 1.3 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ + + +// I2C registers wishbone addresses + +// bitcontroller states +`define I2C_CMD_NOP 4'b0000 +`define I2C_CMD_START 4'b0001 +`define I2C_CMD_STOP 4'b0010 +`define I2C_CMD_WRITE 4'b0100 +`define I2C_CMD_READ 4'b1000 diff --git a/tests/i2c_bench/i2c_master_top.v b/tests/i2c_bench/i2c_master_top.v new file mode 100644 index 000000000..5e9cde7e0 --- /dev/null +++ b/tests/i2c_bench/i2c_master_top.v @@ -0,0 +1,301 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE revB.2 compliant I2C Master controller Top-level //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_top.v,v 1.12 2009-01-19 20:29:26 rherveille Exp $ +// +// $Date: 2009-01-19 20:29:26 $ +// $Revision: 1.12 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// Revision 1.11 2005/02/27 09:26:24 rherveille +// Fixed register overwrite issue. +// Removed full_case pragma, replaced it by a default statement. +// +// Revision 1.10 2003/09/01 10:34:38 rherveille +// Fix a blocking vs. non-blocking error in the wb_dat output mux. +// +// Revision 1.9 2003/01/09 16:44:45 rherveille +// Fixed a bug in the Command Register declaration. +// +// Revision 1.8 2002/12/26 16:05:12 rherveille +// Small code simplifications +// +// Revision 1.7 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.6 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.5 2001/11/10 10:52:55 rherveille +// Changed PRER reset value from 0x0000 to 0xffff, conform specs. +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +`include "i2c_master_defines.v" + +module i2c_master_top( + wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o, + wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o, + scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o ); + + // parameters + parameter ARST_LVL = 1'b0; // asynchronous reset level + + // + // inputs & outputs + // + + // wishbone signals + input wb_clk_i; // master clock input + input wb_rst_i; // synchronous active high reset + input arst_i; // asynchronous reset + input [2:0] wb_adr_i; // lower address bits + input [7:0] wb_dat_i; // databus input + output [7:0] wb_dat_o; // databus output + input wb_we_i; // write enable input + input wb_stb_i; // stobe/core select signal + input wb_cyc_i; // valid bus cycle input + output wb_ack_o; // bus cycle acknowledge output + output wb_inta_o; // interrupt request signal output + + reg [7:0] wb_dat_o; + reg wb_ack_o; + reg wb_inta_o; + + // I2C signals + // i2c clock line + input scl_pad_i; // SCL-line input + output scl_pad_o; // SCL-line output (always 1'b0) + output scl_padoen_o; // SCL-line output enable (active low) + + // i2c data line + input sda_pad_i; // SDA-line input + output sda_pad_o; // SDA-line output (always 1'b0) + output sda_padoen_o; // SDA-line output enable (active low) + + + // + // variable declarations + // + + // registers + reg [15:0] prer; // clock prescale register + reg [ 7:0] ctr; // control register + reg [ 7:0] txr; // transmit register + wire [ 7:0] rxr; // receive register + reg [ 7:0] cr; // command register + wire [ 7:0] sr; // status register + + // done signal: command completed, clear command register + wire done; + + // core enable signal + wire core_en; + wire ien; + + // status register signals + wire irxack; + reg rxack; // received aknowledge from slave + reg tip; // transfer in progress + reg irq_flag; // interrupt pending flag + wire i2c_busy; // bus busy (start signal detected) + wire i2c_al; // i2c bus arbitration lost + reg al; // status register arbitration lost bit + + // + // module body + // + + // generate internal reset + wire rst_i = arst_i ^ ARST_LVL; + + // generate wishbone signals + wire wb_wacc = wb_we_i & wb_ack_o; + + // generate acknowledge output signal + always @(posedge wb_clk_i) + wb_ack_o <= wb_cyc_i & wb_stb_i & ~wb_ack_o; // because timing is always honored + + // assign DAT_O + always @(posedge wb_clk_i) + begin + case (wb_adr_i) // synopsys parallel_case + 3'b000: wb_dat_o <= prer[ 7:0]; + 3'b001: wb_dat_o <= prer[15:8]; + 3'b010: wb_dat_o <= ctr; + 3'b011: wb_dat_o <= rxr; // write is transmit register (txr) + 3'b100: wb_dat_o <= sr; // write is command register (cr) + 3'b101: wb_dat_o <= txr; + 3'b110: wb_dat_o <= cr; + 3'b111: wb_dat_o <= 0; // reserved + default: wb_dat_o <= 16'bx; + endcase + end + + // generate registers + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + prer <= 16'hffff; + ctr <= 8'h0; + txr <= 8'h0; + end + else if (wb_rst_i) + begin + prer <= 16'hffff; + ctr <= 8'h0; + txr <= 8'h0; + end + else + if (wb_wacc) + case (wb_adr_i) // synopsys parallel_case + 3'b000 : prer [ 7:0] <= wb_dat_i; + 3'b001 : prer [15:8] <= wb_dat_i; + 3'b010 : ctr <= wb_dat_i; + 3'b011 : txr <= wb_dat_i; + default: ; + endcase + + // generate command register (special case) + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + cr <= 8'h0; + else if (wb_rst_i) + cr <= 8'h0; + else if (wb_wacc) + begin + if (core_en & (wb_adr_i == 3'b100) ) + cr <= wb_dat_i; + end + else + begin + if (done | i2c_al) + cr[7:4] <= 4'h0; // clear command bits when done + // or when aribitration lost + cr[2:1] <= 2'b0; // reserved bits + cr[0] <= 1'b0; // clear IRQ_ACK bit + end + + + // decode command register + wire sta = cr[7]; + wire sto = cr[6]; + wire rd = cr[5]; + wire wr = cr[4]; + wire ack = cr[3]; + wire iack = cr[0]; + + // decode control register + assign core_en = ctr[7]; + assign ien = ctr[6]; + + // hookup byte controller block + i2c_master_byte_ctrl byte_controller ( + .clk ( wb_clk_i ), + .rst ( wb_rst_i ), + .nReset ( rst_i ), + .ena ( core_en ), + .clk_cnt ( prer ), + .start ( sta ), + .stop ( sto ), + .read ( rd ), + .write ( wr ), + .ack_in ( ack ), + .din ( txr ), + .cmd_ack ( done ), + .ack_out ( irxack ), + .dout ( rxr ), + .i2c_busy ( i2c_busy ), + .i2c_al ( i2c_al ), + .scl_i ( scl_pad_i ), + .scl_o ( scl_pad_o ), + .scl_oen ( scl_padoen_o ), + .sda_i ( sda_pad_i ), + .sda_o ( sda_pad_o ), + .sda_oen ( sda_padoen_o ) + ); + + // status register block + interrupt request signal + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + al <= 1'b0; + rxack <= 1'b0; + tip <= 1'b0; + irq_flag <= 1'b0; + end + else if (wb_rst_i) + begin + al <= 1'b0; + rxack <= 1'b0; + tip <= 1'b0; + irq_flag <= 1'b0; + end + else + begin + al <= i2c_al | (al & ~sta); + rxack <= irxack; + tip <= (rd | wr); + irq_flag <= (done | i2c_al | irq_flag) & ~iack; // interrupt request flag is always generated + end + + // generate interrupt request signals + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + wb_inta_o <= 1'b0; + else if (wb_rst_i) + wb_inta_o <= 1'b0; + else + wb_inta_o <= irq_flag && ien; // interrupt signal is only generated when IEN (interrupt enable bit is set) + + // assign status register bits + assign sr[7] = rxack; + assign sr[6] = i2c_busy; + assign sr[5] = al; + assign sr[4:2] = 3'h0; // reserved + assign sr[1] = tip; + assign sr[0] = irq_flag; + +endmodule diff --git a/tests/i2c_bench/i2c_slave_model.v b/tests/i2c_bench/i2c_slave_model.v new file mode 100644 index 000000000..02b7572cf --- /dev/null +++ b/tests/i2c_bench/i2c_slave_model.v @@ -0,0 +1,361 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant synthesizable I2C Slave model //// +//// //// +//// //// +//// Authors: Richard Herveille (richard@asics.ws) www.asics.ws //// +//// John Sheahan (jrsheahan@optushome.com.au) //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001,2002 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_slave_model.v,v 1.7 2006-09-04 09:08:51 rherveille Exp $ +// +// $Date: 2006-09-04 09:08:51 $ +// $Revision: 1.7 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.6 2005/02/28 11:33:48 rherveille +// Fixed Tsu:sta timing check. +// Added Thd:sta timing check. +// +// Revision 1.5 2003/12/05 11:05:19 rherveille +// Fixed slave address MSB='1' bug +// +// Revision 1.4 2003/09/11 08:25:37 rherveille +// Fixed a bug in the timing section. Changed 'tst_scl' into 'tst_sto'. +// +// Revision 1.3 2002/10/30 18:11:06 rherveille +// Added timing tests to i2c_model. +// Updated testbench. +// +// Revision 1.2 2002/03/17 10:26:38 rherveille +// Fixed some race conditions in the i2c-slave model. +// Added debug information. +// Added headers. +// + +`include "timescale.v" + +module i2c_slave_model (scl, sda); + + // + // parameters + // + parameter I2C_ADR = 7'b001_0000; + + // + // input && outpus + // + input scl; + inout sda; + + // + // Variable declaration + // + wire debug = 1'b1; + + reg [7:0] mem [3:0]; // initiate memory + reg [7:0] mem_adr; // memory address + reg [7:0] mem_do; // memory data output + + reg sta, d_sta; + reg sto, d_sto; + + reg [7:0] sr; // 8bit shift register + reg rw; // read/write direction + + wire my_adr; // my address called ?? + wire i2c_reset; // i2c-statemachine reset + reg [2:0] bit_cnt; // 3bit downcounter + wire acc_done; // 8bits transfered + reg ld; // load downcounter + + reg sda_o; // sda-drive level + wire sda_dly; // delayed version of sda + + // statemachine declaration + parameter idle = 3'b000; + parameter slave_ack = 3'b001; + parameter get_mem_adr = 3'b010; + parameter gma_ack = 3'b011; + parameter data = 3'b100; + parameter data_ack = 3'b101; + + reg [2:0] state; // synopsys enum_state + + // + // module body + // + + initial + begin + sda_o = 1'b1; + state = idle; + mem[0] = 0; + mem[1] = 0; + mem[2] = 0; + mem[3] = 0; + end + + // generate shift register + always @(posedge scl) + sr <= #1 {sr[6:0],sda}; + + //detect my_address + assign my_adr = (sr[7:1] == I2C_ADR); + // FIXME: This should not be a generic assign, but rather + // qualified on address transfer phase and probably reset by stop + + //generate bit-counter + always @(posedge scl) + if(ld) + bit_cnt <= #1 3'b111; + else + bit_cnt <= #1 bit_cnt - 3'h1; + + //generate access done signal + assign acc_done = !(|bit_cnt); + + // generate delayed version of sda + // this model assumes a hold time for sda after the falling edge of scl. + // According to the Phillips i2c spec, there s/b a 0 ns hold time for sda + // with regards to scl. If the data changes coincident with the clock, the + // acknowledge is missed + // Fix by Michael Sosnoski + assign #1 sda_dly = sda; + + + //detect start condition + always @(negedge sda) + if(scl) + begin + sta <= #1 1'b1; + d_sta <= #1 1'b0; + sto <= #1 1'b0; + + if(debug) + $display("DEBUG i2c_slave; start condition detected at %t", $time); + end + else + sta <= #1 1'b0; + + always @(posedge scl) + d_sta <= #1 sta; + + // detect stop condition + always @(posedge sda) + if(scl) + begin + sta <= #1 1'b0; + sto <= #1 1'b1; + + if(debug) + $display("DEBUG i2c_slave; stop condition detected at %t", $time); + end + else + sto <= #1 1'b0; + + //generate i2c_reset signal + assign i2c_reset = sta || sto; + + // generate statemachine + always @(negedge scl or posedge sto) + if (sto || (sta && !d_sta) ) + begin + state <= #1 idle; // reset statemachine + + sda_o <= #1 1'b1; + ld <= #1 1'b1; + end + else + begin + // initial settings + sda_o <= #1 1'b1; + ld <= #1 1'b0; + + case(state) // synopsys full_case parallel_case + idle: // idle state + if (acc_done && my_adr) + begin + state <= #1 slave_ack; + rw <= #1 sr[0]; + sda_o <= #1 1'b0; // generate i2c_ack + + #2; + if(debug && rw) + $display("DEBUG i2c_slave; command byte received (read) at %t", $time); + if(debug && !rw) + $display("DEBUG i2c_slave; command byte received (write) at %t", $time); + + if(rw) + begin + mem_do <= #1 mem[mem_adr % 4]; + + if(debug) + begin + #2 $display("DEBUG i2c_slave; data block read %x from address %x (1)", mem_do, mem_adr); + #2 $display("DEBUG i2c_slave; memcheck [0]=%x, [1]=%x, [2]=%x", mem[4'h0], mem[4'h1], mem[4'h2]); + end + end + end + + slave_ack: + begin + if(rw) + begin + state <= #1 data; + sda_o <= #1 mem_do[7]; + end + else + state <= #1 get_mem_adr; + + ld <= #1 1'b1; + end + + get_mem_adr: // wait for memory address + if(acc_done) + begin + state <= #1 gma_ack; + mem_adr <= #1 sr; // store memory address + sda_o <= #1 !(sr <= 15); // generate i2c_ack, for valid address + + if(debug) + #1 $display("DEBUG i2c_slave; address received. adr=%x, ack=%b", sr, sda_o); + end + + gma_ack: + begin + state <= #1 data; + ld <= #1 1'b1; + end + + data: // receive or drive data + begin + if(rw) + sda_o <= #1 mem_do[7]; + + if(acc_done) + begin + state <= #1 data_ack; + mem_adr <= #2 mem_adr + 8'h1; + sda_o <= #1 (rw && (mem_adr <= 15) ); // send ack on write, receive ack on read + + if(rw) + begin + #3 mem_do <= mem[mem_adr % 4]; + + if(debug) + #5 $display("DEBUG i2c_slave; data block read %x from address %x (2)", mem_do, mem_adr); + end + + if(!rw) + begin + mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory + + if(debug) + #2 $display("DEBUG i2c_slave; data block write %x to address %x", sr, mem_adr); + end + end + end + + data_ack: + begin + ld <= #1 1'b1; + + if(rw) + if(sr[0]) // read operation && master send NACK + begin + state <= #1 idle; + sda_o <= #1 1'b1; + end + else + begin + state <= #1 data; + sda_o <= #1 mem_do[7]; + end + else + begin + state <= #1 data; + sda_o <= #1 1'b1; + end + end + + endcase + end + + // read data from memory + always @(posedge scl) + if(!acc_done && rw) + mem_do <= #1 {mem_do[6:0], 1'b1}; // insert 1'b1 for host ack generation + + // generate tri-states + assign sda = sda_o ? 1'bz : 1'b0; + + + // + // Timing checks + // + + wire tst_sto = sto; + wire tst_sta = sta; + + specify + specparam normal_scl_low = 4700, + normal_scl_high = 4000, + normal_tsu_sta = 4700, + normal_thd_sta = 4000, + normal_tsu_sto = 4000, + normal_tbuf = 4700, + + fast_scl_low = 1300, + fast_scl_high = 600, + fast_tsu_sta = 1300, + fast_thd_sta = 600, + fast_tsu_sto = 600, + fast_tbuf = 1300; + + $width(negedge scl, normal_scl_low); // scl low time + $width(posedge scl, normal_scl_high); // scl high time + + $setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start + $setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start + $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop + + $setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time + endspecify + +endmodule + + diff --git a/tests/i2c_bench/run-test.sh b/tests/i2c_bench/run-test.sh new file mode 100755 index 000000000..5fdbb0594 --- /dev/null +++ b/tests/i2c_bench/run-test.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e +make -C ../.. +../../yosys -l i2c_master_syn.log -o i2c_master_syn.v \ + -p hierarchy -p proc -p memory -p techmap -p opt -p abc -p opt \ + i2c_master_top.v i2c_master_bit_ctrl.v i2c_master_byte_ctrl.v +. /opt/Xilinx/13.4/ISE_DS/settings64.sh + +vlogcomp --work ref i2c_master_bit_ctrl.v +vlogcomp --work ref i2c_master_byte_ctrl.v +vlogcomp --work ref i2c_master_top.v +vlogcomp --work ref i2c_slave_model.v +vlogcomp --work ref spi_slave_model.v +vlogcomp --work ref tst_bench_top.v +vlogcomp --work ref wb_master_model.v +fuse --work ref -o testbench_ref --top tst_bench_top + +cat > testbench_ref.tcl << EOT +vcd dumpfile testbench_ref.vcd +vcd dumpvars -m tst_bench_top -l 0 +vcd dumpon +run 2 ms +exit +EOT + +./testbench_ref -tclbatch testbench_ref.tcl + +vlogcomp --work syn i2c_master_syn.v +vlogcomp --work syn ../../techlibs/simlib.v +vlogcomp --work syn ../../techlibs/stdcells_sim.v +vlogcomp --work syn i2c_slave_model.v +vlogcomp --work syn spi_slave_model.v +vlogcomp --work syn tst_bench_top.v +vlogcomp --work syn wb_master_model.v +fuse --work syn -o testbench_syn --top tst_bench_top + +cat > testbench_syn.tcl << EOT +vcd dumpfile testbench_syn.vcd +vcd dumpvars -m tst_bench_top -l 0 +vcd dumpon +run 2 ms +exit +EOT + +./testbench_syn -tclbatch testbench_syn.tcl + +perl ../tools/vcdcd.pl testbench_ref.vcd testbench_syn.vcd | tee testbench_diff.txt +echo READY. + diff --git a/tests/i2c_bench/spi_slave_model.v b/tests/i2c_bench/spi_slave_model.v new file mode 100644 index 000000000..d49347b0c --- /dev/null +++ b/tests/i2c_bench/spi_slave_model.v @@ -0,0 +1,125 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// SPI Slave Model //// +//// //// +//// //// +//// Authors: Richard Herveille (richard@asics.ws) www.asics.ws //// +//// //// +//// http://www.opencores.org/projects/simple_spi/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2004 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: spi_slave_model.v,v 1.1 2004-02-28 15:32:54 rherveille Exp $ +// +// $Date: 2004-02-28 15:32:54 $ +// $Revision: 1.1 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// +// + + +// Requires: Verilog2001 + +`include "timescale.v" + +module spi_slave_model ( + input wire csn, + input wire sc, + input wire di, + output wire do +); + + // + // Variable declaration + // + wire debug = 1'b1; + + wire cpol = 1'b0; + wire cpha = 1'b0; + + reg [7:0] mem [7:0]; // initiate memory + reg [2:0] mem_adr; // memory address + reg [7:0] mem_do; // memory data output + + reg [7:0] sri, sro; // 8bit shift register + + reg [2:0] bit_cnt; + reg ld; + + wire clk; + + // + // module body + // + + assign clk = cpol ^ cpha ^ sc; + + // generate shift registers + always @(posedge clk) + sri <= #1 {sri[6:0],di}; + + always @(posedge clk) + if (&bit_cnt) + sro <= #1 mem[mem_adr]; + else + sro <= #1 {sro[6:0],1'bx}; + + assign do = sro[7]; + + //generate bit-counter + always @(posedge clk, posedge csn) + if(csn) + bit_cnt <= #1 3'b111; + else + bit_cnt <= #1 bit_cnt - 3'h1; + + //generate access done signal + always @(posedge clk) + ld <= #1 ~(|bit_cnt); + + always @(negedge clk) + if (ld) begin + mem[mem_adr] <= #1 sri; + mem_adr <= #1 mem_adr + 1'b1; + end + + initial + begin + bit_cnt=3'b111; + mem_adr = 0; + sro = mem[mem_adr]; + end +endmodule + + diff --git a/tests/i2c_bench/timescale.v b/tests/i2c_bench/timescale.v new file mode 100644 index 000000000..60d4ecbd1 --- /dev/null +++ b/tests/i2c_bench/timescale.v @@ -0,0 +1,2 @@ +`timescale 1ns / 10ps + diff --git a/tests/i2c_bench/tst_bench_top.v b/tests/i2c_bench/tst_bench_top.v new file mode 100644 index 000000000..9458de054 --- /dev/null +++ b/tests/i2c_bench/tst_bench_top.v @@ -0,0 +1,468 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master controller Testbench //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: tst_bench_top.v,v 1.8 2006-09-04 09:08:51 rherveille Exp $ +// +// $Date: 2006-09-04 09:08:51 $ +// $Revision: 1.8 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.7 2005/02/27 09:24:18 rherveille +// Fixed scl, sda delay. +// +// Revision 1.6 2004/02/28 15:40:42 rherveille +// *** empty log message *** +// +// Revision 1.4 2003/12/05 11:04:38 rherveille +// Added slave address configurability +// +// Revision 1.3 2002/10/30 18:11:06 rherveille +// Added timing tests to i2c_model. +// Updated testbench. +// +// Revision 1.2 2002/03/17 10:26:38 rherveille +// Fixed some race conditions in the i2c-slave model. +// Added debug information. +// Added headers. +// + +`include "timescale.v" + +module tst_bench_top(); + + // + // wires && regs + // + reg clk; + reg rstn; + + wire [31:0] adr; + wire [ 7:0] dat_i, dat_o, dat0_i, dat1_i; + wire we; + wire stb; + wire cyc; + wire ack0, ack1; + wire inta; + + reg [7:0] q, qq; + + wire scl, scl0_o, scl0_oen, scl1_o, scl1_oen; + wire sda, sda0_o, sda0_oen, sda1_o, sda1_oen; + + parameter PRER_LO = 3'b000; + parameter PRER_HI = 3'b001; + parameter CTR = 3'b010; + parameter RXR = 3'b011; + parameter TXR = 3'b011; + parameter CR = 3'b100; + parameter SR = 3'b100; + + parameter TXR_R = 3'b101; // undocumented / reserved output + parameter CR_R = 3'b110; // undocumented / reserved output + + parameter RD = 1'b1; + parameter WR = 1'b0; + parameter SADR = 7'b0010_000; + + // + // Module body + // + + // generate clock + always #5 clk = ~clk; + + // hookup wishbone master model + wb_master_model #(8, 32) u0 ( + .clk(clk), + .rst(rstn), + .adr(adr), + .din(dat_i), + .dout(dat_o), + .cyc(cyc), + .stb(stb), + .we(we), + .sel(), + .ack(ack0 || ack1), + .err(1'b0), + .rty(1'b0) + ); + + wire stb0 = stb & ~adr[3]; + wire stb1 = stb & adr[3]; + + assign dat_i = ({{8'd8}{stb0}} & dat0_i) | ({{8'd8}{stb1}} & dat1_i); + + // hookup wishbone_i2c_master core + i2c_master_top i2c_top ( + + // wishbone interface + .wb_clk_i(clk), + .wb_rst_i(1'b0), + .arst_i(rstn), + .wb_adr_i(adr[2:0]), + .wb_dat_i(dat_o), + .wb_dat_o(dat0_i), + .wb_we_i(we), + .wb_stb_i(stb0), + .wb_cyc_i(cyc), + .wb_ack_o(ack0), + .wb_inta_o(inta), + + // i2c signals + .scl_pad_i(scl), + .scl_pad_o(scl0_o), + .scl_padoen_o(scl0_oen), + .sda_pad_i(sda), + .sda_pad_o(sda0_o), + .sda_padoen_o(sda0_oen) + ), + i2c_top2 ( + + // wishbone interface + .wb_clk_i(clk), + .wb_rst_i(1'b0), + .arst_i(rstn), + .wb_adr_i(adr[2:0]), + .wb_dat_i(dat_o), + .wb_dat_o(dat1_i), + .wb_we_i(we), + .wb_stb_i(stb1), + .wb_cyc_i(cyc), + .wb_ack_o(ack1), + .wb_inta_o(inta), + + // i2c signals + .scl_pad_i(scl), + .scl_pad_o(scl1_o), + .scl_padoen_o(scl1_oen), + .sda_pad_i(sda), + .sda_pad_o(sda1_o), + .sda_padoen_o(sda1_oen) + ); + + + // hookup i2c slave model + i2c_slave_model #(SADR) i2c_slave ( + .scl(scl), + .sda(sda) + ); + + //assign scl = ~((!scl0_oen && !scl0_o) || (!scl1_oen && !scl1_o)); + //assign sda = ~((!sda0_oen && !sda0_o) || (!sda1_oen && !sda1_o)); + + // create i2c lines + delay m0_scl (scl0_oen ? 1'bz : scl0_o, scl), + m1_scl (scl1_oen ? 1'bz : scl1_o, scl), + m0_sda (sda0_oen ? 1'bz : sda0_o, sda), + m1_sda (sda1_oen ? 1'bz : sda1_o, sda); + + pullup p1(scl); // pullup scl line + pullup p2(sda); // pullup sda line + + initial + begin + `ifdef WAVES + $shm_open("waves"); + $shm_probe("AS",tst_bench_top,"AS"); + $display("INFO: Signal dump enabled ...\n\n"); + `endif + +// force i2c_slave.debug = 1'b1; // enable i2c_slave debug information + force i2c_slave.debug = 1'b0; // disable i2c_slave debug information + + $display("\nstatus: %t Testbench started\n\n", $time); + +// $dumpfile("bench.vcd"); +// $dumpvars(1, tst_bench_top); +// $dumpvars(1, tst_bench_top.i2c_slave); + + // initially values + clk = 0; + + // reset system + rstn = 1'b1; // negate reset + #2; + rstn = 1'b0; // assert reset + #1000; + repeat(1) @(posedge clk); + rstn = 1'b1; // negate reset + + $display("status: %t done reset", $time); + + @(posedge clk); + + // + // program core + // + + // program internal registers + u0.wb_write(1, PRER_LO, 8'hfa); // load prescaler lo-byte + u0.wb_write(1, PRER_LO, 8'hc8); // load prescaler lo-byte + u0.wb_write(1, PRER_HI, 8'h00); // load prescaler hi-byte + $display("status: %t programmed registers", $time); + + u0.wb_cmp(0, PRER_LO, 8'hc8); // verify prescaler lo-byte + u0.wb_cmp(0, PRER_HI, 8'h00); // verify prescaler hi-byte + $display("status: %t verified registers", $time); + + u0.wb_write(1, CTR, 8'h80); // enable core + $display("status: %t core enabled", $time); + + // + // access slave (write) + // + + // drive slave address + u0.wb_write(1, TXR, {SADR,WR} ); // present slave address, set write-bit + u0.wb_write(0, CR, 8'h90 ); // set command (start, write) + $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} ); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(0, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // send memory address + u0.wb_write(1, TXR, 8'h01); // present slave's memory address + u0.wb_write(0, CR, 8'h10); // set command (write) + $display("status: %t write slave memory address 01", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(0, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // send memory contents + u0.wb_write(1, TXR, 8'ha5); // present data + u0.wb_write(0, CR, 8'h10); // set command (write) + $display("status: %t write data a5", $time); + +while (scl) #1; +force scl= 1'b0; +#100000; +release scl; + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // send memory contents for next memory address (auto_inc) + u0.wb_write(1, TXR, 8'h5a); // present data + u0.wb_write(0, CR, 8'h50); // set command (stop, write) + $display("status: %t write next data 5a, generate 'stop'", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // + // delay + // +// #100000; // wait for 100us. +// $display("status: %t wait 100us", $time); + + // + // access slave (read) + // + + // drive slave address + u0.wb_write(1, TXR,{SADR,WR} ); // present slave address, set write-bit + u0.wb_write(0, CR, 8'h90 ); // set command (start, write) + $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} ); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // send memory address + u0.wb_write(1, TXR, 8'h01); // present slave's memory address + u0.wb_write(0, CR, 8'h10); // set command (write) + $display("status: %t write slave address 01", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // drive slave address + u0.wb_write(1, TXR, {SADR,RD} ); // present slave's address, set read-bit + u0.wb_write(0, CR, 8'h90 ); // set command (start, write) + $display("status: %t generate 'repeated start', write cmd %0h (slave address+read)", $time, {SADR,RD} ); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // read data from slave + u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) + $display("status: %t read + ack", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // check data just received + u0.wb_read(1, RXR, qq); + if(qq !== 8'ha5) + $display("\nERROR: Expected a5, received %x at time %t", qq, $time); + else + $display("status: %t received %x", $time, qq); + + // read data from slave + u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) + $display("status: %t read + ack", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // check data just received + u0.wb_read(1, RXR, qq); + if(qq !== 8'h5a) + $display("\nERROR: Expected 5a, received %x at time %t", qq, $time); + else + $display("status: %t received %x", $time, qq); + + // read data from slave + u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) + $display("status: %t read + ack", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // check data just received + u0.wb_read(1, RXR, qq); + $display("status: %t received %x from 3rd read address", $time, qq); + + // read data from slave + u0.wb_write(1, CR, 8'h28); // set command (read, nack_read) + $display("status: %t read + nack", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // check data just received + u0.wb_read(1, RXR, qq); + $display("status: %t received %x from 4th read address", $time, qq); + + // + // check invalid slave memory address + // + + // drive slave address + u0.wb_write(1, TXR, {SADR,WR} ); // present slave address, set write-bit + u0.wb_write(0, CR, 8'h90 ); // set command (start, write) + $display("status: %t generate 'start', write cmd %0h (slave address+write). Check invalid address", $time, {SADR,WR} ); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // send memory address + u0.wb_write(1, TXR, 8'h10); // present slave's memory address + u0.wb_write(0, CR, 8'h10); // set command (write) + $display("status: %t write slave memory address 10", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + // slave should have send NACK + $display("status: %t Check for nack", $time); + if(!q[7]) + $display("\nERROR: Expected NACK, received ACK\n"); + + // read data from slave + u0.wb_write(1, CR, 8'h40); // set command (stop) + $display("status: %t generate 'stop'", $time); + + // check tip bit + u0.wb_read(1, SR, q); + while(q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + $display("status: %t tip==0", $time); + + #250000; // wait 250us + $display("\n\nstatus: %t Testbench done", $time); + $finish; + end + +endmodule + +module delay (in, out); + input in; + output out; + + assign out = in; + + specify + (in => out) = (599,599); + endspecify +endmodule + + diff --git a/tests/i2c_bench/wb_master_model.v b/tests/i2c_bench/wb_master_model.v new file mode 100644 index 000000000..65a9b7988 --- /dev/null +++ b/tests/i2c_bench/wb_master_model.v @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 Wishbone Master model //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/mem_ctrl //// +//// //// +/////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +/////////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: wb_master_model.v,v 1.4 2004-02-28 15:40:42 rherveille Exp $ +// +// $Date: 2004-02-28 15:40:42 $ +// $Revision: 1.4 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// +`include "timescale.v" + +module wb_master_model(clk, rst, adr, din, dout, cyc, stb, we, sel, ack, err, rty); + +parameter dwidth = 32; +parameter awidth = 32; + +input clk, rst; +output [awidth -1:0] adr; +input [dwidth -1:0] din; +output [dwidth -1:0] dout; +output cyc, stb; +output we; +output [dwidth/8 -1:0] sel; +input ack, err, rty; + +//////////////////////////////////////////////////////////////////// +// +// Local Wires +// + +reg [awidth -1:0] adr; +reg [dwidth -1:0] dout; +reg cyc, stb; +reg we; +reg [dwidth/8 -1:0] sel; + +reg [dwidth -1:0] q; + +//////////////////////////////////////////////////////////////////// +// +// Memory Logic +// + +initial + begin + //adr = 32'hxxxx_xxxx; + //adr = 0; + adr = {awidth{1'bx}}; + dout = {dwidth{1'bx}}; + cyc = 1'b0; + stb = 1'bx; + we = 1'hx; + sel = {dwidth/8{1'bx}}; + #1; + $display("\nINFO: WISHBONE MASTER MODEL INSTANTIATED (%m)\n"); + end + +//////////////////////////////////////////////////////////////////// +// +// Wishbone write cycle +// + +task wb_write; + input delay; + integer delay; + + input [awidth -1:0] a; + input [dwidth -1:0] d; + + begin + + // wait initial delay + repeat(delay) @(posedge clk); + + // assert wishbone signal + #1; + adr = a; + dout = d; + cyc = 1'b1; + stb = 1'b1; + we = 1'b1; + sel = {dwidth/8{1'b1}}; + @(posedge clk); + + // wait for acknowledge from slave + while(~ack) @(posedge clk); + + // negate wishbone signals + #1; + cyc = 1'b0; + stb = 1'bx; + adr = {awidth{1'bx}}; + dout = {dwidth{1'bx}}; + we = 1'hx; + sel = {dwidth/8{1'bx}}; + + end +endtask + +//////////////////////////////////////////////////////////////////// +// +// Wishbone read cycle +// + +task wb_read; + input delay; + integer delay; + + input [awidth -1:0] a; + output [dwidth -1:0] d; + + begin + + // wait initial delay + repeat(delay) @(posedge clk); + + // assert wishbone signals + #1; + adr = a; + dout = {dwidth{1'bx}}; + cyc = 1'b1; + stb = 1'b1; + we = 1'b0; + sel = {dwidth/8{1'b1}}; + @(posedge clk); + + // wait for acknowledge from slave + while(~ack) @(posedge clk); + + // negate wishbone signals + #1; + cyc = 1'b0; + stb = 1'bx; + adr = {awidth{1'bx}}; + dout = {dwidth{1'bx}}; + we = 1'hx; + sel = {dwidth/8{1'bx}}; + d = din; + + end +endtask + +//////////////////////////////////////////////////////////////////// +// +// Wishbone compare cycle (read data from location and compare with expected data) +// + +task wb_cmp; + input delay; + integer delay; + + input [awidth -1:0] a; + input [dwidth -1:0] d_exp; + + begin + wb_read (delay, a, q); + + if (d_exp !== q) + $display("Data compare error. Received %h, expected %h at time %t", q, d_exp, $time); + end +endtask + +endmodule + + diff --git a/tests/iwls2005/README b/tests/iwls2005/README new file mode 100644 index 000000000..f44a89d93 --- /dev/null +++ b/tests/iwls2005/README @@ -0,0 +1,7 @@ + +A collection of smaller rtl examples from the IWLS 2005 benchmark [1]. +We have no testbenches for these but we can check if we can +parse and synthesize them. + +[1] http://iwls.org/iwls2005/benchmarks.html + diff --git a/tests/iwls2005/aes_core/aes_cipher_top.v b/tests/iwls2005/aes_core/aes_cipher_top.v new file mode 100644 index 000000000..a0acaeb48 --- /dev/null +++ b/tests/iwls2005/aes_core/aes_cipher_top.v @@ -0,0 +1,256 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// AES Cipher Top Level //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/aes_core/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: aes_cipher_top.v,v 1.1.1.1 2002/11/09 11:22:48 rudi Exp $ +// +// $Date: 2002/11/09 11:22:48 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: aes_cipher_top.v,v $ +// Revision 1.1.1.1 2002/11/09 11:22:48 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +module aes_cipher_top(clk, rst, ld, done, key, text_in, text_out ); +input clk, rst; +input ld; +output done; +input [127:0] key; +input [127:0] text_in; +output [127:0] text_out; + +//////////////////////////////////////////////////////////////////// +// +// Local Wires +// + +wire [31:0] w0, w1, w2, w3; +reg [127:0] text_in_r; +reg [127:0] text_out; +reg [7:0] sa00, sa01, sa02, sa03; +reg [7:0] sa10, sa11, sa12, sa13; +reg [7:0] sa20, sa21, sa22, sa23; +reg [7:0] sa30, sa31, sa32, sa33; +wire [7:0] sa00_next, sa01_next, sa02_next, sa03_next; +wire [7:0] sa10_next, sa11_next, sa12_next, sa13_next; +wire [7:0] sa20_next, sa21_next, sa22_next, sa23_next; +wire [7:0] sa30_next, sa31_next, sa32_next, sa33_next; +wire [7:0] sa00_sub, sa01_sub, sa02_sub, sa03_sub; +wire [7:0] sa10_sub, sa11_sub, sa12_sub, sa13_sub; +wire [7:0] sa20_sub, sa21_sub, sa22_sub, sa23_sub; +wire [7:0] sa30_sub, sa31_sub, sa32_sub, sa33_sub; +wire [7:0] sa00_sr, sa01_sr, sa02_sr, sa03_sr; +wire [7:0] sa10_sr, sa11_sr, sa12_sr, sa13_sr; +wire [7:0] sa20_sr, sa21_sr, sa22_sr, sa23_sr; +wire [7:0] sa30_sr, sa31_sr, sa32_sr, sa33_sr; +wire [7:0] sa00_mc, sa01_mc, sa02_mc, sa03_mc; +wire [7:0] sa10_mc, sa11_mc, sa12_mc, sa13_mc; +wire [7:0] sa20_mc, sa21_mc, sa22_mc, sa23_mc; +wire [7:0] sa30_mc, sa31_mc, sa32_mc, sa33_mc; +reg done, ld_r; +reg [3:0] dcnt; + +//////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +always @(posedge clk) + if(!rst) dcnt <= #1 4'h0; + else + if(ld) dcnt <= #1 4'hb; + else + if(|dcnt) dcnt <= #1 dcnt - 4'h1; + +always @(posedge clk) done <= #1 !(|dcnt[3:1]) & dcnt[0] & !ld; +always @(posedge clk) if(ld) text_in_r <= #1 text_in; +always @(posedge clk) ld_r <= #1 ld; + +//////////////////////////////////////////////////////////////////// +// +// Initial Permutation (AddRoundKey) +// + +always @(posedge clk) sa33 <= #1 ld_r ? text_in_r[007:000] ^ w3[07:00] : sa33_next; +always @(posedge clk) sa23 <= #1 ld_r ? text_in_r[015:008] ^ w3[15:08] : sa23_next; +always @(posedge clk) sa13 <= #1 ld_r ? text_in_r[023:016] ^ w3[23:16] : sa13_next; +always @(posedge clk) sa03 <= #1 ld_r ? text_in_r[031:024] ^ w3[31:24] : sa03_next; +always @(posedge clk) sa32 <= #1 ld_r ? text_in_r[039:032] ^ w2[07:00] : sa32_next; +always @(posedge clk) sa22 <= #1 ld_r ? text_in_r[047:040] ^ w2[15:08] : sa22_next; +always @(posedge clk) sa12 <= #1 ld_r ? text_in_r[055:048] ^ w2[23:16] : sa12_next; +always @(posedge clk) sa02 <= #1 ld_r ? text_in_r[063:056] ^ w2[31:24] : sa02_next; +always @(posedge clk) sa31 <= #1 ld_r ? text_in_r[071:064] ^ w1[07:00] : sa31_next; +always @(posedge clk) sa21 <= #1 ld_r ? text_in_r[079:072] ^ w1[15:08] : sa21_next; +always @(posedge clk) sa11 <= #1 ld_r ? text_in_r[087:080] ^ w1[23:16] : sa11_next; +always @(posedge clk) sa01 <= #1 ld_r ? text_in_r[095:088] ^ w1[31:24] : sa01_next; +always @(posedge clk) sa30 <= #1 ld_r ? text_in_r[103:096] ^ w0[07:00] : sa30_next; +always @(posedge clk) sa20 <= #1 ld_r ? text_in_r[111:104] ^ w0[15:08] : sa20_next; +always @(posedge clk) sa10 <= #1 ld_r ? text_in_r[119:112] ^ w0[23:16] : sa10_next; +always @(posedge clk) sa00 <= #1 ld_r ? text_in_r[127:120] ^ w0[31:24] : sa00_next; + +//////////////////////////////////////////////////////////////////// +// +// Round Permutations +// + +assign sa00_sr = sa00_sub; +assign sa01_sr = sa01_sub; +assign sa02_sr = sa02_sub; +assign sa03_sr = sa03_sub; +assign sa10_sr = sa11_sub; +assign sa11_sr = sa12_sub; +assign sa12_sr = sa13_sub; +assign sa13_sr = sa10_sub; +assign sa20_sr = sa22_sub; +assign sa21_sr = sa23_sub; +assign sa22_sr = sa20_sub; +assign sa23_sr = sa21_sub; +assign sa30_sr = sa33_sub; +assign sa31_sr = sa30_sub; +assign sa32_sr = sa31_sub; +assign sa33_sr = sa32_sub; +assign {sa00_mc, sa10_mc, sa20_mc, sa30_mc} = mix_col(sa00_sr,sa10_sr,sa20_sr,sa30_sr); +assign {sa01_mc, sa11_mc, sa21_mc, sa31_mc} = mix_col(sa01_sr,sa11_sr,sa21_sr,sa31_sr); +assign {sa02_mc, sa12_mc, sa22_mc, sa32_mc} = mix_col(sa02_sr,sa12_sr,sa22_sr,sa32_sr); +assign {sa03_mc, sa13_mc, sa23_mc, sa33_mc} = mix_col(sa03_sr,sa13_sr,sa23_sr,sa33_sr); +assign sa00_next = sa00_mc ^ w0[31:24]; +assign sa01_next = sa01_mc ^ w1[31:24]; +assign sa02_next = sa02_mc ^ w2[31:24]; +assign sa03_next = sa03_mc ^ w3[31:24]; +assign sa10_next = sa10_mc ^ w0[23:16]; +assign sa11_next = sa11_mc ^ w1[23:16]; +assign sa12_next = sa12_mc ^ w2[23:16]; +assign sa13_next = sa13_mc ^ w3[23:16]; +assign sa20_next = sa20_mc ^ w0[15:08]; +assign sa21_next = sa21_mc ^ w1[15:08]; +assign sa22_next = sa22_mc ^ w2[15:08]; +assign sa23_next = sa23_mc ^ w3[15:08]; +assign sa30_next = sa30_mc ^ w0[07:00]; +assign sa31_next = sa31_mc ^ w1[07:00]; +assign sa32_next = sa32_mc ^ w2[07:00]; +assign sa33_next = sa33_mc ^ w3[07:00]; + +//////////////////////////////////////////////////////////////////// +// +// Final text output +// + +always @(posedge clk) text_out[127:120] <= #1 sa00_sr ^ w0[31:24]; +always @(posedge clk) text_out[095:088] <= #1 sa01_sr ^ w1[31:24]; +always @(posedge clk) text_out[063:056] <= #1 sa02_sr ^ w2[31:24]; +always @(posedge clk) text_out[031:024] <= #1 sa03_sr ^ w3[31:24]; +always @(posedge clk) text_out[119:112] <= #1 sa10_sr ^ w0[23:16]; +always @(posedge clk) text_out[087:080] <= #1 sa11_sr ^ w1[23:16]; +always @(posedge clk) text_out[055:048] <= #1 sa12_sr ^ w2[23:16]; +always @(posedge clk) text_out[023:016] <= #1 sa13_sr ^ w3[23:16]; +always @(posedge clk) text_out[111:104] <= #1 sa20_sr ^ w0[15:08]; +always @(posedge clk) text_out[079:072] <= #1 sa21_sr ^ w1[15:08]; +always @(posedge clk) text_out[047:040] <= #1 sa22_sr ^ w2[15:08]; +always @(posedge clk) text_out[015:008] <= #1 sa23_sr ^ w3[15:08]; +always @(posedge clk) text_out[103:096] <= #1 sa30_sr ^ w0[07:00]; +always @(posedge clk) text_out[071:064] <= #1 sa31_sr ^ w1[07:00]; +always @(posedge clk) text_out[039:032] <= #1 sa32_sr ^ w2[07:00]; +always @(posedge clk) text_out[007:000] <= #1 sa33_sr ^ w3[07:00]; + +//////////////////////////////////////////////////////////////////// +// +// Generic Functions +// + +function [31:0] mix_col; +input [7:0] s0,s1,s2,s3; +reg [7:0] s0_o,s1_o,s2_o,s3_o; +begin +mix_col[31:24]=xtime(s0)^xtime(s1)^s1^s2^s3; +mix_col[23:16]=s0^xtime(s1)^xtime(s2)^s2^s3; +mix_col[15:08]=s0^s1^xtime(s2)^xtime(s3)^s3; +mix_col[07:00]=xtime(s0)^s0^s1^s2^xtime(s3); +end +endfunction + +function [7:0] xtime; +input [7:0] b; xtime={b[6:0],1'b0}^(8'h1b&{8{b[7]}}); +endfunction + +//////////////////////////////////////////////////////////////////// +// +// Modules +// + +aes_key_expand_128 u0( + .clk( clk ), + .kld( ld ), + .key( key ), + .wo_0( w0 ), + .wo_1( w1 ), + .wo_2( w2 ), + .wo_3( w3 )); + +aes_sbox us00( .a( sa00 ), .d( sa00_sub )); +aes_sbox us01( .a( sa01 ), .d( sa01_sub )); +aes_sbox us02( .a( sa02 ), .d( sa02_sub )); +aes_sbox us03( .a( sa03 ), .d( sa03_sub )); +aes_sbox us10( .a( sa10 ), .d( sa10_sub )); +aes_sbox us11( .a( sa11 ), .d( sa11_sub )); +aes_sbox us12( .a( sa12 ), .d( sa12_sub )); +aes_sbox us13( .a( sa13 ), .d( sa13_sub )); +aes_sbox us20( .a( sa20 ), .d( sa20_sub )); +aes_sbox us21( .a( sa21 ), .d( sa21_sub )); +aes_sbox us22( .a( sa22 ), .d( sa22_sub )); +aes_sbox us23( .a( sa23 ), .d( sa23_sub )); +aes_sbox us30( .a( sa30 ), .d( sa30_sub )); +aes_sbox us31( .a( sa31 ), .d( sa31_sub )); +aes_sbox us32( .a( sa32 ), .d( sa32_sub )); +aes_sbox us33( .a( sa33 ), .d( sa33_sub )); + +endmodule + + diff --git a/tests/iwls2005/aes_core/aes_inv_cipher_top.v b/tests/iwls2005/aes_core/aes_inv_cipher_top.v new file mode 100644 index 000000000..51b3525ac --- /dev/null +++ b/tests/iwls2005/aes_core/aes_inv_cipher_top.v @@ -0,0 +1,327 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// AES Inverse Cipher Top Level //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/aes_core/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: aes_inv_cipher_top.v,v 1.1.1.1 2002/11/09 11:22:53 rudi Exp $ +// +// $Date: 2002/11/09 11:22:53 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: aes_inv_cipher_top.v,v $ +// Revision 1.1.1.1 2002/11/09 11:22:53 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +module aes_inv_cipher_top(clk, rst, kld, ld, done, key, text_in, text_out ); +input clk, rst; +input kld, ld; +output done; +input [127:0] key; +input [127:0] text_in; +output [127:0] text_out; + +//////////////////////////////////////////////////////////////////// +// +// Local Wires +// + +wire [31:0] wk0, wk1, wk2, wk3; +reg [31:0] w0, w1, w2, w3; +reg [127:0] text_in_r; +reg [127:0] text_out; +reg [7:0] sa00, sa01, sa02, sa03; +reg [7:0] sa10, sa11, sa12, sa13; +reg [7:0] sa20, sa21, sa22, sa23; +reg [7:0] sa30, sa31, sa32, sa33; +wire [7:0] sa00_next, sa01_next, sa02_next, sa03_next; +wire [7:0] sa10_next, sa11_next, sa12_next, sa13_next; +wire [7:0] sa20_next, sa21_next, sa22_next, sa23_next; +wire [7:0] sa30_next, sa31_next, sa32_next, sa33_next; +wire [7:0] sa00_sub, sa01_sub, sa02_sub, sa03_sub; +wire [7:0] sa10_sub, sa11_sub, sa12_sub, sa13_sub; +wire [7:0] sa20_sub, sa21_sub, sa22_sub, sa23_sub; +wire [7:0] sa30_sub, sa31_sub, sa32_sub, sa33_sub; +wire [7:0] sa00_sr, sa01_sr, sa02_sr, sa03_sr; +wire [7:0] sa10_sr, sa11_sr, sa12_sr, sa13_sr; +wire [7:0] sa20_sr, sa21_sr, sa22_sr, sa23_sr; +wire [7:0] sa30_sr, sa31_sr, sa32_sr, sa33_sr; +wire [7:0] sa00_ark, sa01_ark, sa02_ark, sa03_ark; +wire [7:0] sa10_ark, sa11_ark, sa12_ark, sa13_ark; +wire [7:0] sa20_ark, sa21_ark, sa22_ark, sa23_ark; +wire [7:0] sa30_ark, sa31_ark, sa32_ark, sa33_ark; +reg ld_r, go, done; +reg [3:0] dcnt; + +//////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +always @(posedge clk) + if(!rst) dcnt <= #1 4'h0; + else + if(done) dcnt <= #1 4'h0; + else + if(ld) dcnt <= #1 4'h1; + else + if(go) dcnt <= #1 dcnt + 4'h1; + +always @(posedge clk) done <= #1 (dcnt==4'hb) & !ld; + +always @(posedge clk) + if(!rst) go <= #1 1'b0; + else + if(ld) go <= #1 1'b1; + else + if(done) go <= #1 1'b0; + +always @(posedge clk) if(ld) text_in_r <= #1 text_in; + +always @(posedge clk) ld_r <= #1 ld; + +//////////////////////////////////////////////////////////////////// +// +// Initial Permutation +// + +always @(posedge clk) sa33 <= #1 ld_r ? text_in_r[007:000] ^ w3[07:00] : sa33_next; +always @(posedge clk) sa23 <= #1 ld_r ? text_in_r[015:008] ^ w3[15:08] : sa23_next; +always @(posedge clk) sa13 <= #1 ld_r ? text_in_r[023:016] ^ w3[23:16] : sa13_next; +always @(posedge clk) sa03 <= #1 ld_r ? text_in_r[031:024] ^ w3[31:24] : sa03_next; +always @(posedge clk) sa32 <= #1 ld_r ? text_in_r[039:032] ^ w2[07:00] : sa32_next; +always @(posedge clk) sa22 <= #1 ld_r ? text_in_r[047:040] ^ w2[15:08] : sa22_next; +always @(posedge clk) sa12 <= #1 ld_r ? text_in_r[055:048] ^ w2[23:16] : sa12_next; +always @(posedge clk) sa02 <= #1 ld_r ? text_in_r[063:056] ^ w2[31:24] : sa02_next; +always @(posedge clk) sa31 <= #1 ld_r ? text_in_r[071:064] ^ w1[07:00] : sa31_next; +always @(posedge clk) sa21 <= #1 ld_r ? text_in_r[079:072] ^ w1[15:08] : sa21_next; +always @(posedge clk) sa11 <= #1 ld_r ? text_in_r[087:080] ^ w1[23:16] : sa11_next; +always @(posedge clk) sa01 <= #1 ld_r ? text_in_r[095:088] ^ w1[31:24] : sa01_next; +always @(posedge clk) sa30 <= #1 ld_r ? text_in_r[103:096] ^ w0[07:00] : sa30_next; +always @(posedge clk) sa20 <= #1 ld_r ? text_in_r[111:104] ^ w0[15:08] : sa20_next; +always @(posedge clk) sa10 <= #1 ld_r ? text_in_r[119:112] ^ w0[23:16] : sa10_next; +always @(posedge clk) sa00 <= #1 ld_r ? text_in_r[127:120] ^ w0[31:24] : sa00_next; + +//////////////////////////////////////////////////////////////////// +// +// Round Permutations +// + +assign sa00_sr = sa00; +assign sa01_sr = sa01; +assign sa02_sr = sa02; +assign sa03_sr = sa03; +assign sa10_sr = sa13; +assign sa11_sr = sa10; +assign sa12_sr = sa11; +assign sa13_sr = sa12; +assign sa20_sr = sa22; +assign sa21_sr = sa23; +assign sa22_sr = sa20; +assign sa23_sr = sa21; +assign sa30_sr = sa31; +assign sa31_sr = sa32; +assign sa32_sr = sa33; +assign sa33_sr = sa30; +assign sa00_ark = sa00_sub ^ w0[31:24]; +assign sa01_ark = sa01_sub ^ w1[31:24]; +assign sa02_ark = sa02_sub ^ w2[31:24]; +assign sa03_ark = sa03_sub ^ w3[31:24]; +assign sa10_ark = sa10_sub ^ w0[23:16]; +assign sa11_ark = sa11_sub ^ w1[23:16]; +assign sa12_ark = sa12_sub ^ w2[23:16]; +assign sa13_ark = sa13_sub ^ w3[23:16]; +assign sa20_ark = sa20_sub ^ w0[15:08]; +assign sa21_ark = sa21_sub ^ w1[15:08]; +assign sa22_ark = sa22_sub ^ w2[15:08]; +assign sa23_ark = sa23_sub ^ w3[15:08]; +assign sa30_ark = sa30_sub ^ w0[07:00]; +assign sa31_ark = sa31_sub ^ w1[07:00]; +assign sa32_ark = sa32_sub ^ w2[07:00]; +assign sa33_ark = sa33_sub ^ w3[07:00]; +assign {sa00_next, sa10_next, sa20_next, sa30_next} = inv_mix_col(sa00_ark,sa10_ark,sa20_ark,sa30_ark); +assign {sa01_next, sa11_next, sa21_next, sa31_next} = inv_mix_col(sa01_ark,sa11_ark,sa21_ark,sa31_ark); +assign {sa02_next, sa12_next, sa22_next, sa32_next} = inv_mix_col(sa02_ark,sa12_ark,sa22_ark,sa32_ark); +assign {sa03_next, sa13_next, sa23_next, sa33_next} = inv_mix_col(sa03_ark,sa13_ark,sa23_ark,sa33_ark); + +//////////////////////////////////////////////////////////////////// +// +// Final Text Output +// + +always @(posedge clk) text_out[127:120] <= #1 sa00_ark; +always @(posedge clk) text_out[095:088] <= #1 sa01_ark; +always @(posedge clk) text_out[063:056] <= #1 sa02_ark; +always @(posedge clk) text_out[031:024] <= #1 sa03_ark; +always @(posedge clk) text_out[119:112] <= #1 sa10_ark; +always @(posedge clk) text_out[087:080] <= #1 sa11_ark; +always @(posedge clk) text_out[055:048] <= #1 sa12_ark; +always @(posedge clk) text_out[023:016] <= #1 sa13_ark; +always @(posedge clk) text_out[111:104] <= #1 sa20_ark; +always @(posedge clk) text_out[079:072] <= #1 sa21_ark; +always @(posedge clk) text_out[047:040] <= #1 sa22_ark; +always @(posedge clk) text_out[015:008] <= #1 sa23_ark; +always @(posedge clk) text_out[103:096] <= #1 sa30_ark; +always @(posedge clk) text_out[071:064] <= #1 sa31_ark; +always @(posedge clk) text_out[039:032] <= #1 sa32_ark; +always @(posedge clk) text_out[007:000] <= #1 sa33_ark; + +//////////////////////////////////////////////////////////////////// +// +// Generic Functions +// + +function [31:0] inv_mix_col; +input [7:0] s0,s1,s2,s3; +begin +inv_mix_col[31:24]=pmul_e(s0)^pmul_b(s1)^pmul_d(s2)^pmul_9(s3); +inv_mix_col[23:16]=pmul_9(s0)^pmul_e(s1)^pmul_b(s2)^pmul_d(s3); +inv_mix_col[15:08]=pmul_d(s0)^pmul_9(s1)^pmul_e(s2)^pmul_b(s3); +inv_mix_col[07:00]=pmul_b(s0)^pmul_d(s1)^pmul_9(s2)^pmul_e(s3); +end +endfunction + +// Some synthesis tools don't like xtime being called recursevly ... +function [7:0] pmul_e; +input [7:0] b; +reg [7:0] two,four,eight; +begin +two=xtime(b);four=xtime(two);eight=xtime(four);pmul_e=eight^four^two; +end +endfunction + +function [7:0] pmul_9; +input [7:0] b; +reg [7:0] two,four,eight; +begin +two=xtime(b);four=xtime(two);eight=xtime(four);pmul_9=eight^b; +end +endfunction + +function [7:0] pmul_d; +input [7:0] b; +reg [7:0] two,four,eight; +begin +two=xtime(b);four=xtime(two);eight=xtime(four);pmul_d=eight^four^b; +end +endfunction + +function [7:0] pmul_b; +input [7:0] b; +reg [7:0] two,four,eight; +begin +two=xtime(b);four=xtime(two);eight=xtime(four);pmul_b=eight^two^b; +end +endfunction + +function [7:0] xtime; +input [7:0] b;xtime={b[6:0],1'b0}^(8'h1b&{8{b[7]}}); +endfunction + +//////////////////////////////////////////////////////////////////// +// +// Key Buffer +// + +reg [127:0] kb[10:0]; +reg [3:0] kcnt; +reg kdone; +reg kb_ld; + +always @(posedge clk) + if(!rst) kcnt <= #1 4'ha; + else + if(kld) kcnt <= #1 4'ha; + else + if(kb_ld) kcnt <= #1 kcnt - 4'h1; + +always @(posedge clk) + if(!rst) kb_ld <= #1 1'b0; + else + if(kld) kb_ld <= #1 1'b1; + else + if(kcnt==4'h0) kb_ld <= #1 1'b0; + +always @(posedge clk) kdone <= #1 (kcnt==4'h0) & !kld; +always @(posedge clk) if(kb_ld) kb[kcnt] <= #1 {wk3, wk2, wk1, wk0}; +always @(posedge clk) {w3, w2, w1, w0} <= #1 kb[dcnt]; + +//////////////////////////////////////////////////////////////////// +// +// Modules +// + +aes_key_expand_128 u0( + .clk( clk ), + .kld( kld ), + .key( key ), + .wo_0( wk0 ), + .wo_1( wk1 ), + .wo_2( wk2 ), + .wo_3( wk3 )); + +aes_inv_sbox us00( .a( sa00_sr ), .d( sa00_sub )); +aes_inv_sbox us01( .a( sa01_sr ), .d( sa01_sub )); +aes_inv_sbox us02( .a( sa02_sr ), .d( sa02_sub )); +aes_inv_sbox us03( .a( sa03_sr ), .d( sa03_sub )); +aes_inv_sbox us10( .a( sa10_sr ), .d( sa10_sub )); +aes_inv_sbox us11( .a( sa11_sr ), .d( sa11_sub )); +aes_inv_sbox us12( .a( sa12_sr ), .d( sa12_sub )); +aes_inv_sbox us13( .a( sa13_sr ), .d( sa13_sub )); +aes_inv_sbox us20( .a( sa20_sr ), .d( sa20_sub )); +aes_inv_sbox us21( .a( sa21_sr ), .d( sa21_sub )); +aes_inv_sbox us22( .a( sa22_sr ), .d( sa22_sub )); +aes_inv_sbox us23( .a( sa23_sr ), .d( sa23_sub )); +aes_inv_sbox us30( .a( sa30_sr ), .d( sa30_sub )); +aes_inv_sbox us31( .a( sa31_sr ), .d( sa31_sub )); +aes_inv_sbox us32( .a( sa32_sr ), .d( sa32_sub )); +aes_inv_sbox us33( .a( sa33_sr ), .d( sa33_sub )); + +endmodule + diff --git a/tests/iwls2005/aes_core/aes_inv_sbox.v b/tests/iwls2005/aes_core/aes_inv_sbox.v new file mode 100644 index 000000000..323181eba --- /dev/null +++ b/tests/iwls2005/aes_core/aes_inv_sbox.v @@ -0,0 +1,328 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// AES Inverse SBOX (ROM) //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/aes_core/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: aes_inv_sbox.v,v 1.1.1.1 2002/11/09 11:22:55 rudi Exp $ +// +// $Date: 2002/11/09 11:22:55 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: aes_inv_sbox.v,v $ +// Revision 1.1.1.1 2002/11/09 11:22:55 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +module aes_inv_sbox(a,d); +input [7:0] a; +output [7:0] d; +reg [7:0] d; + +always @(a) + case(a) // synopsys full_case parallel_case + 8'h00: d=8'h52; + 8'h01: d=8'h09; + 8'h02: d=8'h6a; + 8'h03: d=8'hd5; + 8'h04: d=8'h30; + 8'h05: d=8'h36; + 8'h06: d=8'ha5; + 8'h07: d=8'h38; + 8'h08: d=8'hbf; + 8'h09: d=8'h40; + 8'h0a: d=8'ha3; + 8'h0b: d=8'h9e; + 8'h0c: d=8'h81; + 8'h0d: d=8'hf3; + 8'h0e: d=8'hd7; + 8'h0f: d=8'hfb; + 8'h10: d=8'h7c; + 8'h11: d=8'he3; + 8'h12: d=8'h39; + 8'h13: d=8'h82; + 8'h14: d=8'h9b; + 8'h15: d=8'h2f; + 8'h16: d=8'hff; + 8'h17: d=8'h87; + 8'h18: d=8'h34; + 8'h19: d=8'h8e; + 8'h1a: d=8'h43; + 8'h1b: d=8'h44; + 8'h1c: d=8'hc4; + 8'h1d: d=8'hde; + 8'h1e: d=8'he9; + 8'h1f: d=8'hcb; + 8'h20: d=8'h54; + 8'h21: d=8'h7b; + 8'h22: d=8'h94; + 8'h23: d=8'h32; + 8'h24: d=8'ha6; + 8'h25: d=8'hc2; + 8'h26: d=8'h23; + 8'h27: d=8'h3d; + 8'h28: d=8'hee; + 8'h29: d=8'h4c; + 8'h2a: d=8'h95; + 8'h2b: d=8'h0b; + 8'h2c: d=8'h42; + 8'h2d: d=8'hfa; + 8'h2e: d=8'hc3; + 8'h2f: d=8'h4e; + 8'h30: d=8'h08; + 8'h31: d=8'h2e; + 8'h32: d=8'ha1; + 8'h33: d=8'h66; + 8'h34: d=8'h28; + 8'h35: d=8'hd9; + 8'h36: d=8'h24; + 8'h37: d=8'hb2; + 8'h38: d=8'h76; + 8'h39: d=8'h5b; + 8'h3a: d=8'ha2; + 8'h3b: d=8'h49; + 8'h3c: d=8'h6d; + 8'h3d: d=8'h8b; + 8'h3e: d=8'hd1; + 8'h3f: d=8'h25; + 8'h40: d=8'h72; + 8'h41: d=8'hf8; + 8'h42: d=8'hf6; + 8'h43: d=8'h64; + 8'h44: d=8'h86; + 8'h45: d=8'h68; + 8'h46: d=8'h98; + 8'h47: d=8'h16; + 8'h48: d=8'hd4; + 8'h49: d=8'ha4; + 8'h4a: d=8'h5c; + 8'h4b: d=8'hcc; + 8'h4c: d=8'h5d; + 8'h4d: d=8'h65; + 8'h4e: d=8'hb6; + 8'h4f: d=8'h92; + 8'h50: d=8'h6c; + 8'h51: d=8'h70; + 8'h52: d=8'h48; + 8'h53: d=8'h50; + 8'h54: d=8'hfd; + 8'h55: d=8'hed; + 8'h56: d=8'hb9; + 8'h57: d=8'hda; + 8'h58: d=8'h5e; + 8'h59: d=8'h15; + 8'h5a: d=8'h46; + 8'h5b: d=8'h57; + 8'h5c: d=8'ha7; + 8'h5d: d=8'h8d; + 8'h5e: d=8'h9d; + 8'h5f: d=8'h84; + 8'h60: d=8'h90; + 8'h61: d=8'hd8; + 8'h62: d=8'hab; + 8'h63: d=8'h00; + 8'h64: d=8'h8c; + 8'h65: d=8'hbc; + 8'h66: d=8'hd3; + 8'h67: d=8'h0a; + 8'h68: d=8'hf7; + 8'h69: d=8'he4; + 8'h6a: d=8'h58; + 8'h6b: d=8'h05; + 8'h6c: d=8'hb8; + 8'h6d: d=8'hb3; + 8'h6e: d=8'h45; + 8'h6f: d=8'h06; + 8'h70: d=8'hd0; + 8'h71: d=8'h2c; + 8'h72: d=8'h1e; + 8'h73: d=8'h8f; + 8'h74: d=8'hca; + 8'h75: d=8'h3f; + 8'h76: d=8'h0f; + 8'h77: d=8'h02; + 8'h78: d=8'hc1; + 8'h79: d=8'haf; + 8'h7a: d=8'hbd; + 8'h7b: d=8'h03; + 8'h7c: d=8'h01; + 8'h7d: d=8'h13; + 8'h7e: d=8'h8a; + 8'h7f: d=8'h6b; + 8'h80: d=8'h3a; + 8'h81: d=8'h91; + 8'h82: d=8'h11; + 8'h83: d=8'h41; + 8'h84: d=8'h4f; + 8'h85: d=8'h67; + 8'h86: d=8'hdc; + 8'h87: d=8'hea; + 8'h88: d=8'h97; + 8'h89: d=8'hf2; + 8'h8a: d=8'hcf; + 8'h8b: d=8'hce; + 8'h8c: d=8'hf0; + 8'h8d: d=8'hb4; + 8'h8e: d=8'he6; + 8'h8f: d=8'h73; + 8'h90: d=8'h96; + 8'h91: d=8'hac; + 8'h92: d=8'h74; + 8'h93: d=8'h22; + 8'h94: d=8'he7; + 8'h95: d=8'had; + 8'h96: d=8'h35; + 8'h97: d=8'h85; + 8'h98: d=8'he2; + 8'h99: d=8'hf9; + 8'h9a: d=8'h37; + 8'h9b: d=8'he8; + 8'h9c: d=8'h1c; + 8'h9d: d=8'h75; + 8'h9e: d=8'hdf; + 8'h9f: d=8'h6e; + 8'ha0: d=8'h47; + 8'ha1: d=8'hf1; + 8'ha2: d=8'h1a; + 8'ha3: d=8'h71; + 8'ha4: d=8'h1d; + 8'ha5: d=8'h29; + 8'ha6: d=8'hc5; + 8'ha7: d=8'h89; + 8'ha8: d=8'h6f; + 8'ha9: d=8'hb7; + 8'haa: d=8'h62; + 8'hab: d=8'h0e; + 8'hac: d=8'haa; + 8'had: d=8'h18; + 8'hae: d=8'hbe; + 8'haf: d=8'h1b; + 8'hb0: d=8'hfc; + 8'hb1: d=8'h56; + 8'hb2: d=8'h3e; + 8'hb3: d=8'h4b; + 8'hb4: d=8'hc6; + 8'hb5: d=8'hd2; + 8'hb6: d=8'h79; + 8'hb7: d=8'h20; + 8'hb8: d=8'h9a; + 8'hb9: d=8'hdb; + 8'hba: d=8'hc0; + 8'hbb: d=8'hfe; + 8'hbc: d=8'h78; + 8'hbd: d=8'hcd; + 8'hbe: d=8'h5a; + 8'hbf: d=8'hf4; + 8'hc0: d=8'h1f; + 8'hc1: d=8'hdd; + 8'hc2: d=8'ha8; + 8'hc3: d=8'h33; + 8'hc4: d=8'h88; + 8'hc5: d=8'h07; + 8'hc6: d=8'hc7; + 8'hc7: d=8'h31; + 8'hc8: d=8'hb1; + 8'hc9: d=8'h12; + 8'hca: d=8'h10; + 8'hcb: d=8'h59; + 8'hcc: d=8'h27; + 8'hcd: d=8'h80; + 8'hce: d=8'hec; + 8'hcf: d=8'h5f; + 8'hd0: d=8'h60; + 8'hd1: d=8'h51; + 8'hd2: d=8'h7f; + 8'hd3: d=8'ha9; + 8'hd4: d=8'h19; + 8'hd5: d=8'hb5; + 8'hd6: d=8'h4a; + 8'hd7: d=8'h0d; + 8'hd8: d=8'h2d; + 8'hd9: d=8'he5; + 8'hda: d=8'h7a; + 8'hdb: d=8'h9f; + 8'hdc: d=8'h93; + 8'hdd: d=8'hc9; + 8'hde: d=8'h9c; + 8'hdf: d=8'hef; + 8'he0: d=8'ha0; + 8'he1: d=8'he0; + 8'he2: d=8'h3b; + 8'he3: d=8'h4d; + 8'he4: d=8'hae; + 8'he5: d=8'h2a; + 8'he6: d=8'hf5; + 8'he7: d=8'hb0; + 8'he8: d=8'hc8; + 8'he9: d=8'heb; + 8'hea: d=8'hbb; + 8'heb: d=8'h3c; + 8'hec: d=8'h83; + 8'hed: d=8'h53; + 8'hee: d=8'h99; + 8'hef: d=8'h61; + 8'hf0: d=8'h17; + 8'hf1: d=8'h2b; + 8'hf2: d=8'h04; + 8'hf3: d=8'h7e; + 8'hf4: d=8'hba; + 8'hf5: d=8'h77; + 8'hf6: d=8'hd6; + 8'hf7: d=8'h26; + 8'hf8: d=8'he1; + 8'hf9: d=8'h69; + 8'hfa: d=8'h14; + 8'hfb: d=8'h63; + 8'hfc: d=8'h55; + 8'hfd: d=8'h21; + 8'hfe: d=8'h0c; + 8'hff: d=8'h7d; + endcase +endmodule + + diff --git a/tests/iwls2005/aes_core/aes_key_expand_128.v b/tests/iwls2005/aes_core/aes_key_expand_128.v new file mode 100644 index 000000000..ddc74b732 --- /dev/null +++ b/tests/iwls2005/aes_core/aes_key_expand_128.v @@ -0,0 +1,87 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// AES Key Expand Block (for 128 bit keys) //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/aes_core/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: aes_key_expand_128.v,v 1.1.1.1 2002/11/09 11:22:38 rudi Exp $ +// +// $Date: 2002/11/09 11:22:38 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: aes_key_expand_128.v,v $ +// Revision 1.1.1.1 2002/11/09 11:22:38 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +module aes_key_expand_128(clk, kld, key, wo_0, wo_1, wo_2, wo_3); +input clk; +input kld; +input [127:0] key; +output [31:0] wo_0, wo_1, wo_2, wo_3; +reg [31:0] w[3:0]; +wire [31:0] tmp_w; +wire [31:0] subword; +wire [31:0] rcon; + +assign wo_0 = w[0]; +assign wo_1 = w[1]; +assign wo_2 = w[2]; +assign wo_3 = w[3]; +always @(posedge clk) w[0] <= #1 kld ? key[127:096] : w[0]^subword^rcon; +always @(posedge clk) w[1] <= #1 kld ? key[095:064] : w[0]^w[1]^subword^rcon; +always @(posedge clk) w[2] <= #1 kld ? key[063:032] : w[0]^w[2]^w[1]^subword^rcon; +always @(posedge clk) w[3] <= #1 kld ? key[031:000] : w[0]^w[3]^w[2]^w[1]^subword^rcon; +assign tmp_w = w[3]; +aes_sbox u0( .a(tmp_w[23:16]), .d(subword[31:24])); +aes_sbox u1( .a(tmp_w[15:08]), .d(subword[23:16])); +aes_sbox u2( .a(tmp_w[07:00]), .d(subword[15:08])); +aes_sbox u3( .a(tmp_w[31:24]), .d(subword[07:00])); +aes_rcon r0( .clk(clk), .kld(kld), .out(rcon)); +endmodule + diff --git a/tests/iwls2005/aes_core/aes_rcon.v b/tests/iwls2005/aes_core/aes_rcon.v new file mode 100644 index 000000000..c2c0a1242 --- /dev/null +++ b/tests/iwls2005/aes_core/aes_rcon.v @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// AES RCON Block //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/aes_core/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: aes_rcon.v,v 1.1.1.1 2002/11/09 11:22:38 rudi Exp $ +// +// $Date: 2002/11/09 11:22:38 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: aes_rcon.v,v $ +// Revision 1.1.1.1 2002/11/09 11:22:38 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +module aes_rcon(clk, kld, out); +input clk; +input kld; +output [31:0] out; +reg [31:0] out; +reg [3:0] rcnt; +wire [3:0] rcnt_next; + +always @(posedge clk) + if(kld) out <= #1 32'h01_00_00_00; + else out <= #1 frcon(rcnt_next); + +assign rcnt_next = rcnt + 4'h1; +always @(posedge clk) + if(kld) rcnt <= #1 4'h0; + else rcnt <= #1 rcnt_next; + +function [31:0] frcon; +input [3:0] i; +case(i) // synopsys parallel_case + 4'h0: frcon=32'h01_00_00_00; + 4'h1: frcon=32'h02_00_00_00; + 4'h2: frcon=32'h04_00_00_00; + 4'h3: frcon=32'h08_00_00_00; + 4'h4: frcon=32'h10_00_00_00; + 4'h5: frcon=32'h20_00_00_00; + 4'h6: frcon=32'h40_00_00_00; + 4'h7: frcon=32'h80_00_00_00; + 4'h8: frcon=32'h1b_00_00_00; + 4'h9: frcon=32'h36_00_00_00; + default: frcon=32'h00_00_00_00; +endcase +endfunction + +endmodule diff --git a/tests/iwls2005/aes_core/aes_sbox.v b/tests/iwls2005/aes_core/aes_sbox.v new file mode 100644 index 000000000..e01d75ef8 --- /dev/null +++ b/tests/iwls2005/aes_core/aes_sbox.v @@ -0,0 +1,329 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// AES SBOX (ROM) //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/aes_core/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: aes_sbox.v,v 1.1.1.1 2002/11/09 11:22:38 rudi Exp $ +// +// $Date: 2002/11/09 11:22:38 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: aes_sbox.v,v $ +// Revision 1.1.1.1 2002/11/09 11:22:38 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +module aes_sbox(a,d); +input [7:0] a; +output [7:0] d; +reg [7:0] d; + +always @(a) + case(a) // synopsys full_case parallel_case + 8'h00: d=8'h63; + 8'h01: d=8'h7c; + 8'h02: d=8'h77; + 8'h03: d=8'h7b; + 8'h04: d=8'hf2; + 8'h05: d=8'h6b; + 8'h06: d=8'h6f; + 8'h07: d=8'hc5; + 8'h08: d=8'h30; + 8'h09: d=8'h01; + 8'h0a: d=8'h67; + 8'h0b: d=8'h2b; + 8'h0c: d=8'hfe; + 8'h0d: d=8'hd7; + 8'h0e: d=8'hab; + 8'h0f: d=8'h76; + 8'h10: d=8'hca; + 8'h11: d=8'h82; + 8'h12: d=8'hc9; + 8'h13: d=8'h7d; + 8'h14: d=8'hfa; + 8'h15: d=8'h59; + 8'h16: d=8'h47; + 8'h17: d=8'hf0; + 8'h18: d=8'had; + 8'h19: d=8'hd4; + 8'h1a: d=8'ha2; + 8'h1b: d=8'haf; + 8'h1c: d=8'h9c; + 8'h1d: d=8'ha4; + 8'h1e: d=8'h72; + 8'h1f: d=8'hc0; + 8'h20: d=8'hb7; + 8'h21: d=8'hfd; + 8'h22: d=8'h93; + 8'h23: d=8'h26; + 8'h24: d=8'h36; + 8'h25: d=8'h3f; + 8'h26: d=8'hf7; + 8'h27: d=8'hcc; + 8'h28: d=8'h34; + 8'h29: d=8'ha5; + 8'h2a: d=8'he5; + 8'h2b: d=8'hf1; + 8'h2c: d=8'h71; + 8'h2d: d=8'hd8; + 8'h2e: d=8'h31; + 8'h2f: d=8'h15; + 8'h30: d=8'h04; + 8'h31: d=8'hc7; + 8'h32: d=8'h23; + 8'h33: d=8'hc3; + 8'h34: d=8'h18; + 8'h35: d=8'h96; + 8'h36: d=8'h05; + 8'h37: d=8'h9a; + 8'h38: d=8'h07; + 8'h39: d=8'h12; + 8'h3a: d=8'h80; + 8'h3b: d=8'he2; + 8'h3c: d=8'heb; + 8'h3d: d=8'h27; + 8'h3e: d=8'hb2; + 8'h3f: d=8'h75; + 8'h40: d=8'h09; + 8'h41: d=8'h83; + 8'h42: d=8'h2c; + 8'h43: d=8'h1a; + 8'h44: d=8'h1b; + 8'h45: d=8'h6e; + 8'h46: d=8'h5a; + 8'h47: d=8'ha0; + 8'h48: d=8'h52; + 8'h49: d=8'h3b; + 8'h4a: d=8'hd6; + 8'h4b: d=8'hb3; + 8'h4c: d=8'h29; + 8'h4d: d=8'he3; + 8'h4e: d=8'h2f; + 8'h4f: d=8'h84; + 8'h50: d=8'h53; + 8'h51: d=8'hd1; + 8'h52: d=8'h00; + 8'h53: d=8'hed; + 8'h54: d=8'h20; + 8'h55: d=8'hfc; + 8'h56: d=8'hb1; + 8'h57: d=8'h5b; + 8'h58: d=8'h6a; + 8'h59: d=8'hcb; + 8'h5a: d=8'hbe; + 8'h5b: d=8'h39; + 8'h5c: d=8'h4a; + 8'h5d: d=8'h4c; + 8'h5e: d=8'h58; + 8'h5f: d=8'hcf; + 8'h60: d=8'hd0; + 8'h61: d=8'hef; + 8'h62: d=8'haa; + 8'h63: d=8'hfb; + 8'h64: d=8'h43; + 8'h65: d=8'h4d; + 8'h66: d=8'h33; + 8'h67: d=8'h85; + 8'h68: d=8'h45; + 8'h69: d=8'hf9; + 8'h6a: d=8'h02; + 8'h6b: d=8'h7f; + 8'h6c: d=8'h50; + 8'h6d: d=8'h3c; + 8'h6e: d=8'h9f; + 8'h6f: d=8'ha8; + 8'h70: d=8'h51; + 8'h71: d=8'ha3; + 8'h72: d=8'h40; + 8'h73: d=8'h8f; + 8'h74: d=8'h92; + 8'h75: d=8'h9d; + 8'h76: d=8'h38; + 8'h77: d=8'hf5; + 8'h78: d=8'hbc; + 8'h79: d=8'hb6; + 8'h7a: d=8'hda; + 8'h7b: d=8'h21; + 8'h7c: d=8'h10; + 8'h7d: d=8'hff; + 8'h7e: d=8'hf3; + 8'h7f: d=8'hd2; + 8'h80: d=8'hcd; + 8'h81: d=8'h0c; + 8'h82: d=8'h13; + 8'h83: d=8'hec; + 8'h84: d=8'h5f; + 8'h85: d=8'h97; + 8'h86: d=8'h44; + 8'h87: d=8'h17; + 8'h88: d=8'hc4; + 8'h89: d=8'ha7; + 8'h8a: d=8'h7e; + 8'h8b: d=8'h3d; + 8'h8c: d=8'h64; + 8'h8d: d=8'h5d; + 8'h8e: d=8'h19; + 8'h8f: d=8'h73; + 8'h90: d=8'h60; + 8'h91: d=8'h81; + 8'h92: d=8'h4f; + 8'h93: d=8'hdc; + 8'h94: d=8'h22; + 8'h95: d=8'h2a; + 8'h96: d=8'h90; + 8'h97: d=8'h88; + 8'h98: d=8'h46; + 8'h99: d=8'hee; + 8'h9a: d=8'hb8; + 8'h9b: d=8'h14; + 8'h9c: d=8'hde; + 8'h9d: d=8'h5e; + 8'h9e: d=8'h0b; + 8'h9f: d=8'hdb; + 8'ha0: d=8'he0; + 8'ha1: d=8'h32; + 8'ha2: d=8'h3a; + 8'ha3: d=8'h0a; + 8'ha4: d=8'h49; + 8'ha5: d=8'h06; + 8'ha6: d=8'h24; + 8'ha7: d=8'h5c; + 8'ha8: d=8'hc2; + 8'ha9: d=8'hd3; + 8'haa: d=8'hac; + 8'hab: d=8'h62; + 8'hac: d=8'h91; + 8'had: d=8'h95; + 8'hae: d=8'he4; + 8'haf: d=8'h79; + 8'hb0: d=8'he7; + 8'hb1: d=8'hc8; + 8'hb2: d=8'h37; + 8'hb3: d=8'h6d; + 8'hb4: d=8'h8d; + 8'hb5: d=8'hd5; + 8'hb6: d=8'h4e; + 8'hb7: d=8'ha9; + 8'hb8: d=8'h6c; + 8'hb9: d=8'h56; + 8'hba: d=8'hf4; + 8'hbb: d=8'hea; + 8'hbc: d=8'h65; + 8'hbd: d=8'h7a; + 8'hbe: d=8'hae; + 8'hbf: d=8'h08; + 8'hc0: d=8'hba; + 8'hc1: d=8'h78; + 8'hc2: d=8'h25; + 8'hc3: d=8'h2e; + 8'hc4: d=8'h1c; + 8'hc5: d=8'ha6; + 8'hc6: d=8'hb4; + 8'hc7: d=8'hc6; + 8'hc8: d=8'he8; + 8'hc9: d=8'hdd; + 8'hca: d=8'h74; + 8'hcb: d=8'h1f; + 8'hcc: d=8'h4b; + 8'hcd: d=8'hbd; + 8'hce: d=8'h8b; + 8'hcf: d=8'h8a; + 8'hd0: d=8'h70; + 8'hd1: d=8'h3e; + 8'hd2: d=8'hb5; + 8'hd3: d=8'h66; + 8'hd4: d=8'h48; + 8'hd5: d=8'h03; + 8'hd6: d=8'hf6; + 8'hd7: d=8'h0e; + 8'hd8: d=8'h61; + 8'hd9: d=8'h35; + 8'hda: d=8'h57; + 8'hdb: d=8'hb9; + 8'hdc: d=8'h86; + 8'hdd: d=8'hc1; + 8'hde: d=8'h1d; + 8'hdf: d=8'h9e; + 8'he0: d=8'he1; + 8'he1: d=8'hf8; + 8'he2: d=8'h98; + 8'he3: d=8'h11; + 8'he4: d=8'h69; + 8'he5: d=8'hd9; + 8'he6: d=8'h8e; + 8'he7: d=8'h94; + 8'he8: d=8'h9b; + 8'he9: d=8'h1e; + 8'hea: d=8'h87; + 8'heb: d=8'he9; + 8'hec: d=8'hce; + 8'hed: d=8'h55; + 8'hee: d=8'h28; + 8'hef: d=8'hdf; + 8'hf0: d=8'h8c; + 8'hf1: d=8'ha1; + 8'hf2: d=8'h89; + 8'hf3: d=8'h0d; + 8'hf4: d=8'hbf; + 8'hf5: d=8'he6; + 8'hf6: d=8'h42; + 8'hf7: d=8'h68; + 8'hf8: d=8'h41; + 8'hf9: d=8'h99; + 8'hfa: d=8'h2d; + 8'hfb: d=8'h0f; + 8'hfc: d=8'hb0; + 8'hfd: d=8'h54; + 8'hfe: d=8'hbb; + 8'hff: d=8'h16; + endcase + +endmodule + + diff --git a/tests/iwls2005/aes_core/timescale.v b/tests/iwls2005/aes_core/timescale.v new file mode 100644 index 000000000..ff9e265a8 --- /dev/null +++ b/tests/iwls2005/aes_core/timescale.v @@ -0,0 +1 @@ +`timescale 1ns / 10ps diff --git a/tests/iwls2005/fpu/except.v b/tests/iwls2005/fpu/except.v new file mode 100644 index 000000000..007099fe1 --- /dev/null +++ b/tests/iwls2005/fpu/except.v @@ -0,0 +1,153 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// EXCEPT //// +//// Floating Point Exception/Special Numbers Unit //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + + +`timescale 1ns / 100ps + + +module except( clk, opa, opb, inf, ind, qnan, snan, opa_nan, opb_nan, + opa_00, opb_00, opa_inf, opb_inf, opa_dn, opb_dn); +input clk; +input [31:0] opa, opb; +output inf, ind, qnan, snan, opa_nan, opb_nan; +output opa_00, opb_00; +output opa_inf, opb_inf; +output opa_dn; +output opb_dn; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +wire [7:0] expa, expb; // alias to opX exponent +wire [22:0] fracta, fractb; // alias to opX fraction +reg expa_ff, infa_f_r, qnan_r_a, snan_r_a; +reg expb_ff, infb_f_r, qnan_r_b, snan_r_b; +reg inf, ind, qnan, snan; // Output registers +reg opa_nan, opb_nan; +reg expa_00, expb_00, fracta_00, fractb_00; +reg opa_00, opb_00; +reg opa_inf, opb_inf; +reg opa_dn, opb_dn; + +//////////////////////////////////////////////////////////////////////// +// +// Aliases +// + +assign expa = opa[30:23]; +assign expb = opb[30:23]; +assign fracta = opa[22:0]; +assign fractb = opb[22:0]; + +//////////////////////////////////////////////////////////////////////// +// +// Determine if any of the input operators is a INF or NAN or any other special number +// + +always @(posedge clk) + expa_ff <= #1 &expa; + +always @(posedge clk) + expb_ff <= #1 &expb; + +always @(posedge clk) + infa_f_r <= #1 !(|fracta); + +always @(posedge clk) + infb_f_r <= #1 !(|fractb); + +always @(posedge clk) + qnan_r_a <= #1 fracta[22]; + +always @(posedge clk) + snan_r_a <= #1 !fracta[22] & |fracta[21:0]; + +always @(posedge clk) + qnan_r_b <= #1 fractb[22]; + +always @(posedge clk) + snan_r_b <= #1 !fractb[22] & |fractb[21:0]; + +always @(posedge clk) + ind <= #1 (expa_ff & infa_f_r) & (expb_ff & infb_f_r); + +always @(posedge clk) + inf <= #1 (expa_ff & infa_f_r) | (expb_ff & infb_f_r); + +always @(posedge clk) + qnan <= #1 (expa_ff & qnan_r_a) | (expb_ff & qnan_r_b); + +always @(posedge clk) + snan <= #1 (expa_ff & snan_r_a) | (expb_ff & snan_r_b); + +always @(posedge clk) + opa_nan <= #1 &expa & (|fracta[22:0]); + +always @(posedge clk) + opb_nan <= #1 &expb & (|fractb[22:0]); + +always @(posedge clk) + opa_inf <= #1 (expa_ff & infa_f_r); + +always @(posedge clk) + opb_inf <= #1 (expb_ff & infb_f_r); + +always @(posedge clk) + expa_00 <= #1 !(|expa); + +always @(posedge clk) + expb_00 <= #1 !(|expb); + +always @(posedge clk) + fracta_00 <= #1 !(|fracta); + +always @(posedge clk) + fractb_00 <= #1 !(|fractb); + +always @(posedge clk) + opa_00 <= #1 expa_00 & fracta_00; + +always @(posedge clk) + opb_00 <= #1 expb_00 & fractb_00; + +always @(posedge clk) + opa_dn <= #1 expa_00; + +always @(posedge clk) + opb_dn <= #1 expb_00; + +endmodule + diff --git a/tests/iwls2005/fpu/fpu.v b/tests/iwls2005/fpu/fpu.v new file mode 100644 index 000000000..165a1d246 --- /dev/null +++ b/tests/iwls2005/fpu/fpu.v @@ -0,0 +1,560 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// FPU //// +//// Floating Point Unit (Single precision) //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +`timescale 1ns / 100ps + +/* + +FPU Operations (fpu_op): +======================== + +0 = add +1 = sub +2 = mul +3 = div +4 = +5 = +6 = +7 = + +Rounding Modes (rmode): +======================= + +0 = round_nearest_even +1 = round_to_zero +2 = round_up +3 = round_down + +*/ + + +module fpu( clk, rmode, fpu_op, opa, opb, out, inf, snan, qnan, ine, overflow, underflow, zero, div_by_zero); +input clk; +input [1:0] rmode; +input [2:0] fpu_op; +input [31:0] opa, opb; +output [31:0] out; +output inf, snan, qnan; +output ine; +output overflow, underflow; +output zero; +output div_by_zero; + +parameter INF = 31'h7f800000, + QNAN = 31'h7fc00001, + SNAN = 31'h7f800001; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires +// +reg zero; +reg [31:0] opa_r, opb_r; // Input operand registers +reg [31:0] out; // Output register +reg div_by_zero; // Divide by zero output register +wire signa, signb; // alias to opX sign +wire sign_fasu; // sign output +wire [26:0] fracta, fractb; // Fraction Outputs from EQU block +wire [7:0] exp_fasu; // Exponent output from EQU block +reg [7:0] exp_r; // Exponent output (registerd) +wire [26:0] fract_out_d; // fraction output +wire co; // carry output +reg [27:0] fract_out_q; // fraction output (registerd) +wire [30:0] out_d; // Intermediate final result output +wire overflow_d, underflow_d;// Overflow/Underflow Indicators +reg overflow, underflow; // Output registers for Overflow & Underflow +reg inf, snan, qnan; // Output Registers for INF, SNAN and QNAN +reg ine; // Output Registers for INE +reg [1:0] rmode_r1, rmode_r2, // Pipeline registers for rounding mode + rmode_r3; +reg [2:0] fpu_op_r1, fpu_op_r2, // Pipeline registers for fp opration + fpu_op_r3; +wire mul_inf, div_inf; +wire mul_00, div_00; + +//////////////////////////////////////////////////////////////////////// +// +// Input Registers +// + +always @(posedge clk) + opa_r <= #1 opa; + +always @(posedge clk) + opb_r <= #1 opb; + +always @(posedge clk) + rmode_r1 <= #1 rmode; + +always @(posedge clk) + rmode_r2 <= #1 rmode_r1; + +always @(posedge clk) + rmode_r3 <= #1 rmode_r2; + +always @(posedge clk) + fpu_op_r1 <= #1 fpu_op; + +always @(posedge clk) + fpu_op_r2 <= #1 fpu_op_r1; + +always @(posedge clk) + fpu_op_r3 <= #1 fpu_op_r2; + +//////////////////////////////////////////////////////////////////////// +// +// Exceptions block +// +wire inf_d, ind_d, qnan_d, snan_d, opa_nan, opb_nan; +wire opa_00, opb_00; +wire opa_inf, opb_inf; +wire opa_dn, opb_dn; + +except u0( .clk(clk), + .opa(opa_r), .opb(opb_r), + .inf(inf_d), .ind(ind_d), + .qnan(qnan_d), .snan(snan_d), + .opa_nan(opa_nan), .opb_nan(opb_nan), + .opa_00(opa_00), .opb_00(opb_00), + .opa_inf(opa_inf), .opb_inf(opb_inf), + .opa_dn(opa_dn), .opb_dn(opb_dn) + ); + +//////////////////////////////////////////////////////////////////////// +// +// Pre-Normalize block +// - Adjusts the numbers to equal exponents and sorts them +// - determine result sign +// - determine actual operation to perform (add or sub) +// + +wire nan_sign_d, result_zero_sign_d; +reg sign_fasu_r; +wire [7:0] exp_mul; +wire sign_mul; +reg sign_mul_r; +wire [23:0] fracta_mul, fractb_mul; +wire inf_mul; +reg inf_mul_r; +wire [1:0] exp_ovf; +reg [1:0] exp_ovf_r; +wire sign_exe; +reg sign_exe_r; +wire [2:0] underflow_fmul_d; + + +pre_norm u1(.clk(clk), // System Clock + .rmode(rmode_r2), // Roundin Mode + .add(!fpu_op_r1[0]), // Add/Sub Input + .opa(opa_r), .opb(opb_r), // Registered OP Inputs + .opa_nan(opa_nan), // OpA is a NAN indicator + .opb_nan(opb_nan), // OpB is a NAN indicator + .fracta_out(fracta), // Equalized and sorted fraction + .fractb_out(fractb), // outputs (Registered) + .exp_dn_out(exp_fasu), // Selected exponent output (registered); + .sign(sign_fasu), // Encoded output Sign (registered) + .nan_sign(nan_sign_d), // Output Sign for NANs (registered) + .result_zero_sign(result_zero_sign_d), // Output Sign for zero result (registered) + .fasu_op(fasu_op) // Actual fasu operation output (registered) + ); + +always @(posedge clk) + sign_fasu_r <= #1 sign_fasu; + +pre_norm_fmul u2( + .clk(clk), + .fpu_op(fpu_op_r1), + .opa(opa_r), .opb(opb_r), + .fracta(fracta_mul), + .fractb(fractb_mul), + .exp_out(exp_mul), // FMUL exponent output (registered) + .sign(sign_mul), // FMUL sign output (registered) + .sign_exe(sign_exe), // FMUL exception sign output (registered) + .inf(inf_mul), // FMUL inf output (registered) + .exp_ovf(exp_ovf), // FMUL exponnent overflow output (registered) + .underflow(underflow_fmul_d) + ); + + +always @(posedge clk) + sign_mul_r <= #1 sign_mul; + +always @(posedge clk) + sign_exe_r <= #1 sign_exe; + +always @(posedge clk) + inf_mul_r <= #1 inf_mul; + +always @(posedge clk) + exp_ovf_r <= #1 exp_ovf; + + +//////////////////////////////////////////////////////////////////////// +// +// Add/Sub +// + +add_sub27 u3( + .add(fasu_op), // Add/Sub + .opa(fracta), // Fraction A input + .opb(fractb), // Fraction B Input + .sum(fract_out_d), // SUM output + .co(co_d) ); // Carry Output + +always @(posedge clk) + fract_out_q <= #1 {co_d, fract_out_d}; + +//////////////////////////////////////////////////////////////////////// +// +// Mul +// +wire [47:0] prod; + +mul_r2 u5(.clk(clk), .opa(fracta_mul), .opb(fractb_mul), .prod(prod)); + +//////////////////////////////////////////////////////////////////////// +// +// Divide +// +wire [49:0] quo; +wire [49:0] fdiv_opa; +wire [49:0] remainder; +wire remainder_00; +reg [4:0] div_opa_ldz_d, div_opa_ldz_r1, div_opa_ldz_r2; + +always @(fracta_mul) + casex(fracta_mul[22:0]) + 23'b1??????????????????????: div_opa_ldz_d = 1; + 23'b01?????????????????????: div_opa_ldz_d = 2; + 23'b001????????????????????: div_opa_ldz_d = 3; + 23'b0001???????????????????: div_opa_ldz_d = 4; + 23'b00001??????????????????: div_opa_ldz_d = 5; + 23'b000001?????????????????: div_opa_ldz_d = 6; + 23'b0000001????????????????: div_opa_ldz_d = 7; + 23'b00000001???????????????: div_opa_ldz_d = 8; + 23'b000000001??????????????: div_opa_ldz_d = 9; + 23'b0000000001?????????????: div_opa_ldz_d = 10; + 23'b00000000001????????????: div_opa_ldz_d = 11; + 23'b000000000001???????????: div_opa_ldz_d = 12; + 23'b0000000000001??????????: div_opa_ldz_d = 13; + 23'b00000000000001?????????: div_opa_ldz_d = 14; + 23'b000000000000001????????: div_opa_ldz_d = 15; + 23'b0000000000000001???????: div_opa_ldz_d = 16; + 23'b00000000000000001??????: div_opa_ldz_d = 17; + 23'b000000000000000001?????: div_opa_ldz_d = 18; + 23'b0000000000000000001????: div_opa_ldz_d = 19; + 23'b00000000000000000001???: div_opa_ldz_d = 20; + 23'b000000000000000000001??: div_opa_ldz_d = 21; + 23'b0000000000000000000001?: div_opa_ldz_d = 22; + 23'b0000000000000000000000?: div_opa_ldz_d = 23; + endcase + +assign fdiv_opa = !(|opa_r[30:23]) ? {(fracta_mul<f2i_emax)) | (opas & (exp_inf2i_emax)) | (opas & (exp_in8'h16); + +assign f2i_shft = exp_in-8'h7d; + +// Select shifting direction +assign left_right = op_div ? lr_div : op_mul ? lr_mul : 1; + +assign lr_div = (op_dn & !exp_ovf[1] & exp_ovf[0]) ? 1 : + (op_dn & exp_ovf[1]) ? 0 : + (op_dn & div_shft1_co) ? 0 : + (op_dn & exp_out_00) ? 1 : + (!op_dn & exp_out_00 & !exp_ovf[1]) ? 1 : + exp_ovf[1] ? 0 : + 1; +assign lr_mul = (shft_co | (!exp_ovf[1] & exp_in_00) | + (!exp_ovf[1] & !exp_in_00 & (exp_out1_co | exp_out_00) )) ? 1 : + ( exp_ovf[1] | exp_in_00 ) ? 0 : + 1; + +// Select Left and Right shift value +assign fasu_shift = (dn | exp_out_00) ? (exp_in_00 ? 8'h2 : exp_in_pl1[7:0]) : {2'h0, fi_ldz}; +assign shift_right = op_div ? shftr_div : shftr_mul; + +assign conv_shft = op_f2i ? f2i_shft : {2'h0, fi_ldz}; + +assign shift_left = op_div ? shftl_div : op_mul ? shftl_mul : (op_f2i | op_i2f) ? conv_shft : fasu_shift; + +assign shftl_mul = (shft_co | + (!exp_ovf[1] & exp_in_00) | + (!exp_ovf[1] & !exp_in_00 & (exp_out1_co | exp_out_00))) ? exp_in_pl1[7:0] : {2'h0, fi_ldz}; + +assign shftl_div = ( op_dn & exp_out_00 & !(!exp_ovf[1] & exp_ovf[0])) ? div_shft1[7:0] : + (!op_dn & exp_out_00 & !exp_ovf[1]) ? exp_in[7:0] : + {2'h0, fi_ldz}; +assign shftr_div = (op_dn & exp_ovf[1]) ? div_shft3 : + (op_dn & div_shft1_co) ? div_shft4 : + div_shft2; +// Do the actual shifting +assign fract_in_shftr = (|shift_right[7:6]) ? 0 : fract_in>>shift_right[5:0]; +assign fract_in_shftl = (|shift_left[7:6] | (f2i_zero & op_f2i)) ? 0 : fract_in<f2i_emax) ? 0 : opas) : + ((exp_inf2i_emax) ? 1 : opas); + +assign exp_i2f = fract_in_00 ? (opas ? 8'h9e : 0) : (8'h9e-fi_ldz); +assign exp_f2i_1 = {{8{fract_in[47]}}, fract_in }<9'hfe) )) ? div_exp2 : + (opa_dn | (exp_in_00 & !exp_ovf[1]) ) ? 0 : + exp_out1_mi1; + +assign div_inf = opb_dn & !opa_dn & (div_exp1[7:0] < 8'h7f); + +// --------------------------------------------------------------------- +// Round + +// Extract rounding (GRS) bits +assign grs_sel_div = op_div & (exp_ovf[1] | div_dn | exp_out1_co | exp_out_00); + +assign g = grs_sel_div ? fract_out[0] : fract_out[0]; +assign r = grs_sel_div ? (fract_trunc[24] & !div_nr) : fract_trunc[24]; +assign s = grs_sel_div ? |fract_trunc[24:0] : (|fract_trunc[23:0] | (fract_trunc[24] & op_div)); + +// Round to nearest even +assign round = (g & r) | (r & s) ; +assign {exp_rnd_adj0, fract_out_rnd0} = round ? fract_out_pl1 : {1'b0, fract_out}; +assign exp_out_rnd0 = exp_rnd_adj0 ? exp_out_pl1 : exp_out; +assign ovf0 = exp_out_final_ff & !rmode_01 & !op_f2i; + +// round to zero +assign fract_out_rnd1 = (exp_out_ff & !op_div & !dn & !op_f2i) ? 23'h7fffff : fract_out; +assign exp_fix_div = (fi_ldz>22) ? exp_fix_diva : exp_fix_divb; +assign exp_out_rnd1 = (g & r & s & exp_in_ff) ? (op_div ? exp_fix_div : exp_next_mi[7:0]) : + (exp_out_ff & !op_f2i) ? exp_in : exp_out; +assign ovf1 = exp_out_ff & !dn; + +// round to +inf (UP) and -inf (DOWN) +assign r_sign = sign; + +assign round2a = !exp_out_fe | !fract_out_7fffff | (exp_out_fe & fract_out_7fffff); +assign round2_fasu = ((r | s) & !r_sign) & (!exp_out[7] | (exp_out[7] & round2a)); + +assign round2_fmul = !r_sign & + ( + (exp_ovf[1] & !fract_in_00 & + ( ((!exp_out1_co | op_dn) & (r | s | (!rem_00 & op_div) )) | fract_out_00 | (!op_dn & !op_div)) + ) | + ( + (r | s | (!rem_00 & op_div)) & ( + (!exp_ovf[1] & (exp_in_80 | !exp_ovf[0])) | op_div | + ( exp_ovf[1] & !exp_ovf[0] & exp_out1_co) + ) + ) + ); + +assign round2_f2i = rmode_10 & (( |fract_in[23:0] & !opas & (exp_in<8'h80 )) | (|fract_trunc)); +assign round2 = (op_mul | op_div) ? round2_fmul : op_f2i ? round2_f2i : round2_fasu; + +assign {exp_rnd_adj2a, fract_out_rnd2a} = round2 ? fract_out_pl1 : {1'b0, fract_out}; +assign exp_out_rnd2a = exp_rnd_adj2a ? ((exp_ovf[1] & op_mul) ? exp_out_mi1 : exp_out_pl1) : exp_out; + +assign fract_out_rnd2 = (r_sign & exp_out_ff & !op_div & !dn & !op_f2i) ? 23'h7fffff : fract_out_rnd2a; +assign exp_out_rnd2 = (r_sign & exp_out_ff & !op_f2i) ? 8'hfe : exp_out_rnd2a; + + +// Choose rounding mode +always @(rmode or exp_out_rnd0 or exp_out_rnd1 or exp_out_rnd2) + case(rmode) // synopsys full_case parallel_case + 0: exp_out_rnd = exp_out_rnd0; + 1: exp_out_rnd = exp_out_rnd1; + 2,3: exp_out_rnd = exp_out_rnd2; + endcase + +always @(rmode or fract_out_rnd0 or fract_out_rnd1 or fract_out_rnd2) + case(rmode) // synopsys full_case parallel_case + 0: fract_out_rnd = fract_out_rnd0; + 1: fract_out_rnd = fract_out_rnd1; + 2,3: fract_out_rnd = fract_out_rnd2; + endcase + +// --------------------------------------------------------------------- +// Final Output Mux +// Fix Output for denormalized and special numbers +wire max_num, inf_out; + +assign max_num = ( !rmode_00 & (op_mul | op_div ) & ( + ( exp_ovf[1] & exp_ovf[0]) | + (!exp_ovf[1] & !exp_ovf[0] & exp_in_ff & (fi_ldz_2<24) & (exp_out!=8'hfe) ) + ) + ) | + + ( op_div & ( + ( rmode_01 & ( div_inf | + (exp_out_ff & !exp_ovf[1] ) | + (exp_ovf[1] & exp_ovf[0] ) + ) + ) | + + ( rmode[1] & !exp_ovf[1] & ( + ( exp_ovf[0] & exp_in_ff & r_sign & fract_in[47] + ) | + + ( r_sign & ( + (fract_in[47] & div_inf) | + (exp_in[7] & !exp_out_rnd[7] & !exp_in_80 & exp_out!=8'h7f ) | + (exp_in[7] & exp_out_rnd[7] & r_sign & exp_out_ff & op_dn & + div_exp1>9'h0fe ) + ) + ) | + + ( exp_in_00 & r_sign & ( + div_inf | + (r_sign & exp_out_ff & fi_ldz_2<24) + ) + ) + ) + ) + ) + ); + + +assign inf_out = (rmode[1] & (op_mul | op_div) & !r_sign & ( (exp_in_ff & !op_div) | + (exp_ovf[1] & exp_ovf[0] & (exp_in_00 | exp_in[7]) ) + ) + ) | (div_inf & op_div & ( + rmode_00 | + (rmode[1] & !exp_in_ff & !exp_ovf[1] & !exp_ovf[0] & !r_sign ) | + (rmode[1] & !exp_ovf[1] & exp_ovf[0] & exp_in_00 & !r_sign) + ) + ) | (op_div & rmode[1] & exp_in_ff & op_dn & !r_sign & (fi_ldz_2 < 24) & (exp_out_rnd!=8'hfe) ); + +assign fract_out_final = (inf_out | ovf0 | output_zero ) ? 23'h0 : + (max_num | (f2i_max & op_f2i) ) ? 23'h7fffff : + fract_out_rnd; + +assign exp_out_final = ((op_div & exp_ovf[1] & !exp_ovf[0]) | output_zero ) ? 8'h00 : + ((op_div & exp_ovf[1] & exp_ovf[0] & rmode_00) | inf_out | (f2i_max & op_f2i) ) ? 8'hff : + max_num ? 8'hfe : + exp_out_rnd; + + +// --------------------------------------------------------------------- +// Pack Result + +assign out = {exp_out_final, fract_out_final}; + +// --------------------------------------------------------------------- +// Exceptions +wire underflow_fmul; +wire overflow_fdiv; +wire undeflow_div; + +wire z = shft_co | ( exp_ovf[1] | exp_in_00) | + (!exp_ovf[1] & !exp_in_00 & (exp_out1_co | exp_out_00)); + +assign underflow_fmul = ( (|fract_trunc) & z & !exp_in_ff ) | + (fract_out_00 & !fract_in_00 & exp_ovf[1]); + +assign undeflow_div = !(exp_ovf[1] & exp_ovf[0] & rmode_00) & !inf_out & !max_num & exp_out_final!=8'hff & ( + + ((|fract_trunc) & !opb_dn & ( + ( op_dn & !exp_ovf[1] & exp_ovf[0]) | + ( op_dn & exp_ovf[1]) | + ( op_dn & div_shft1_co) | + exp_out_00 | + exp_ovf[1] + ) + + ) | + + ( exp_ovf[1] & !exp_ovf[0] & ( + ( op_dn & exp_in>8'h16 & fi_ldz<23) | + ( op_dn & exp_in<23 & fi_ldz<23 & !rem_00) | + ( !op_dn & (exp_in[7]==exp_div[7]) & !rem_00) | + ( !op_dn & exp_in_00 & (exp_div[7:1]==7'h7f) ) | + ( !op_dn & exp_in<8'h7f & exp_in>8'h20 ) + ) + ) | + + (!exp_ovf[1] & !exp_ovf[0] & ( + ( op_dn & fi_ldz<23 & exp_out_00) | + ( exp_in_00 & !rem_00) | + ( !op_dn & ldz_all<23 & exp_in==1 & exp_out_00 & !rem_00) + ) + ) + + ); + +assign underflow = op_div ? undeflow_div : op_mul ? underflow_fmul : (!fract_in[47] & exp_out1_co) & !dn; + +assign overflow_fdiv = inf_out | + (!rmode_00 & max_num) | + (exp_in[7] & op_dn & exp_out_ff) | + (exp_ovf[0] & (exp_ovf[1] | exp_out_ff) ); + +assign overflow = op_div ? overflow_fdiv : (ovf0 | ovf1); + +wire f2i_ine; + +assign f2i_ine = (f2i_zero & !fract_in_00 & !opas) | + (|fract_trunc) | + (f2i_zero & (exp_in<8'h80) & opas & !fract_in_00) | + (f2i_max & rmode_11 & (exp_in<8'h80)); + + + +assign ine = op_f2i ? f2i_ine : + op_i2f ? (|fract_trunc) : + ((r & !dn) | (s & !dn) | max_num | (op_div & !rem_00)); + +// --------------------------------------------------------------------- +// Debugging Stuff + +// synopsys translate_off + +wire [26:0] fracta_del, fractb_del; +wire [2:0] grs_del; +wire dn_del; +wire [7:0] exp_in_del; +wire [7:0] exp_out_del; +wire [22:0] fract_out_del; +wire [47:0] fract_in_del; +wire overflow_del; +wire [1:0] exp_ovf_del; +wire [22:0] fract_out_x_del, fract_out_rnd2a_del; +wire [24:0] trunc_xx_del; +wire exp_rnd_adj2a_del; +wire [22:0] fract_dn_del; +wire [4:0] div_opa_ldz_del; +wire [23:0] fracta_div_del; +wire [23:0] fractb_div_del; +wire div_inf_del; +wire [7:0] fi_ldz_2_del; +wire inf_out_del, max_out_del; +wire [5:0] fi_ldz_del; +wire rx_del; +wire ez_del; +wire lr; +wire [7:0] shr, shl, exp_div_del; + +delay2 #26 ud000(clk, test.u0.fracta, fracta_del); +delay2 #26 ud001(clk, test.u0.fractb, fractb_del); +delay1 #2 ud002(clk, {g,r,s}, grs_del); +delay1 #0 ud004(clk, dn, dn_del); +delay1 #7 ud005(clk, exp_in, exp_in_del); +delay1 #7 ud007(clk, exp_out_rnd, exp_out_del); +delay1 #47 ud009(clk, fract_in, fract_in_del); +delay1 #0 ud010(clk, overflow, overflow_del); +delay1 #1 ud011(clk, exp_ovf, exp_ovf_del); +delay1 #22 ud014(clk, fract_out, fract_out_x_del); +delay1 #24 ud015(clk, fract_trunc, trunc_xx_del); +delay1 #0 ud017(clk, exp_rnd_adj2a, exp_rnd_adj2a_del); +delay1 #4 ud019(clk, div_opa_ldz, div_opa_ldz_del); +delay3 #23 ud020(clk, test.u0.fdiv_opa[49:26], fracta_div_del); +delay3 #23 ud021(clk, test.u0.fractb_mul, fractb_div_del); +delay1 #0 ud023(clk, div_inf, div_inf_del); +delay1 #7 ud024(clk, fi_ldz_2, fi_ldz_2_del); +delay1 #0 ud025(clk, inf_out, inf_out_del); +delay1 #0 ud026(clk, max_num, max_num_del); +delay1 #5 ud027(clk, fi_ldz, fi_ldz_del); +delay1 #0 ud028(clk, rem_00, rx_del); + +delay1 #0 ud029(clk, left_right, lr); +delay1 #7 ud030(clk, shift_right, shr); +delay1 #7 ud031(clk, shift_left, shl); +delay1 #22 ud032(clk, fract_out_rnd2a, fract_out_rnd2a_del); + +delay1 #7 ud033(clk, exp_div, exp_div_del); + +always @(test.error_event) + begin + + $display("\n----------------------------------------------"); + + $display("ERROR: GRS: %b exp_ovf: %b dn: %h exp_in: %h exp_out: %h, exp_rnd_adj2a: %b", + grs_del, exp_ovf_del, dn_del, exp_in_del, exp_out_del, exp_rnd_adj2a_del); + + $display(" div_opa: %b, div_opb: %b, rem_00: %b, exp_div: %h", + fracta_div_del, fractb_div_del, rx_del, exp_div_del); + + $display(" lr: %b, shl: %h, shr: %h", + lr, shl, shr); + + + $display(" overflow: %b, fract_in=%b fa:%h fb:%h", + overflow_del, fract_in_del, fracta_del, fractb_del); + + $display(" div_opa_ldz: %h, div_inf: %b, inf_out: %b, max_num: %b, fi_ldz: %h, fi_ldz_2: %h", + div_opa_ldz_del, div_inf_del, inf_out_del, max_num_del, fi_ldz_del, fi_ldz_2_del); + + $display(" fract_out_x: %b, fract_out_rnd2a_del: %h, fract_trunc: %b\n", + fract_out_x_del, fract_out_rnd2a_del, trunc_xx_del); + end + + +// synopsys translate_on + +endmodule + +// synopsys translate_off + +module delay1(clk, in, out); +parameter N = 1; +input [N:0] in; +output [N:0] out; +input clk; + +reg [N:0] out; + +always @(posedge clk) + out <= #1 in; + +endmodule + + +module delay2(clk, in, out); +parameter N = 1; +input [N:0] in; +output [N:0] out; +input clk; + +reg [N:0] out, r1; + +always @(posedge clk) + r1 <= #1 in; + +always @(posedge clk) + out <= #1 r1; + +endmodule + +module delay3(clk, in, out); +parameter N = 1; +input [N:0] in; +output [N:0] out; +input clk; + +reg [N:0] out, r1, r2; + +always @(posedge clk) + r1 <= #1 in; + +always @(posedge clk) + r2 <= #1 r1; + +always @(posedge clk) + out <= #1 r2; + +endmodule + +// synopsys translate_on \ No newline at end of file diff --git a/tests/iwls2005/fpu/pre_norm.v b/tests/iwls2005/fpu/pre_norm.v new file mode 100644 index 000000000..c54c71fa2 --- /dev/null +++ b/tests/iwls2005/fpu/pre_norm.v @@ -0,0 +1,270 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Pre Normalize //// +//// Pre Normalization Unit for Add/Sub Operations //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +`timescale 1ns / 100ps + + +module pre_norm(clk, rmode, add, opa, opb, opa_nan, opb_nan, fracta_out, + fractb_out, exp_dn_out, sign, nan_sign, result_zero_sign, + fasu_op); +input clk; +input [1:0] rmode; +input add; +input [31:0] opa, opb; +input opa_nan, opb_nan; +output [26:0] fracta_out, fractb_out; +output [7:0] exp_dn_out; +output sign; +output nan_sign, result_zero_sign; +output fasu_op; // Operation Output + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +wire signa, signb; // alias to opX sign +wire [7:0] expa, expb; // alias to opX exponent +wire [22:0] fracta, fractb; // alias to opX fraction +wire expa_lt_expb; // expa is larger than expb indicator +wire fractb_lt_fracta; // fractb is larger than fracta indicator +reg [7:0] exp_dn_out; // de normalized exponent output +wire [7:0] exp_small, exp_large; +wire [7:0] exp_diff; // Numeric difference of the two exponents +wire [22:0] adj_op; // Fraction adjustment: input +wire [26:0] adj_op_tmp; +wire [26:0] adj_op_out; // Fraction adjustment: output +wire [26:0] fracta_n, fractb_n; // Fraction selection after normalizing +wire [26:0] fracta_s, fractb_s; // Fraction Sorting out +reg [26:0] fracta_out, fractb_out; // Fraction Output +reg sign, sign_d; // Sign Output +reg add_d; // operation (add/sub) +reg fasu_op; // operation (add/sub) register +wire expa_dn, expb_dn; +reg sticky; +reg result_zero_sign; +reg add_r, signa_r, signb_r; +wire [4:0] exp_diff_sft; +wire exp_lt_27; +wire op_dn; +wire [26:0] adj_op_out_sft; +reg fracta_lt_fractb, fracta_eq_fractb; +wire nan_sign1; +reg nan_sign; + +//////////////////////////////////////////////////////////////////////// +// +// Aliases +// + +assign signa = opa[31]; +assign signb = opb[31]; +assign expa = opa[30:23]; +assign expb = opb[30:23]; +assign fracta = opa[22:0]; +assign fractb = opb[22:0]; + +//////////////////////////////////////////////////////////////////////// +// +// Pre-Normalize exponents (and fractions) +// + +assign expa_lt_expb = expa > expb; // expa is larger than expb + +// --------------------------------------------------------------------- +// Normalize + +assign expa_dn = !(|expa); // opa denormalized +assign expb_dn = !(|expb); // opb denormalized + +// --------------------------------------------------------------------- +// Calculate the difference between the smaller and larger exponent + +wire [7:0] exp_diff1, exp_diff1a, exp_diff2; + +assign exp_small = expa_lt_expb ? expb : expa; +assign exp_large = expa_lt_expb ? expa : expb; +assign exp_diff1 = exp_large - exp_small; +assign exp_diff1a = exp_diff1-1; +assign exp_diff2 = (expa_dn | expb_dn) ? exp_diff1a : exp_diff1; +assign exp_diff = (expa_dn & expb_dn) ? 8'h0 : exp_diff2; + +always @(posedge clk) // If numbers are equal we should return zero + exp_dn_out <= #1 (!add_d & expa==expb & fracta==fractb) ? 8'h0 : exp_large; + +// --------------------------------------------------------------------- +// Adjust the smaller fraction + + +assign op_dn = expa_lt_expb ? expb_dn : expa_dn; +assign adj_op = expa_lt_expb ? fractb : fracta; +assign adj_op_tmp = { ~op_dn, adj_op, 3'b0 }; // recover hidden bit (op_dn) + +// adj_op_out is 27 bits wide, so can only be shifted 27 bits to the right +assign exp_lt_27 = exp_diff > 8'd27; +assign exp_diff_sft = exp_lt_27 ? 5'd27 : exp_diff[4:0]; +assign adj_op_out_sft = adj_op_tmp >> exp_diff_sft; +assign adj_op_out = {adj_op_out_sft[26:1], adj_op_out_sft[0] | sticky }; + +// --------------------------------------------------------------------- +// Get truncated portion (sticky bit) + +always @(exp_diff_sft or adj_op_tmp) + case(exp_diff_sft) // synopsys full_case parallel_case + 00: sticky = 1'h0; + 01: sticky = adj_op_tmp[0]; + 02: sticky = |adj_op_tmp[01:0]; + 03: sticky = |adj_op_tmp[02:0]; + 04: sticky = |adj_op_tmp[03:0]; + 05: sticky = |adj_op_tmp[04:0]; + 06: sticky = |adj_op_tmp[05:0]; + 07: sticky = |adj_op_tmp[06:0]; + 08: sticky = |adj_op_tmp[07:0]; + 09: sticky = |adj_op_tmp[08:0]; + 10: sticky = |adj_op_tmp[09:0]; + 11: sticky = |adj_op_tmp[10:0]; + 12: sticky = |adj_op_tmp[11:0]; + 13: sticky = |adj_op_tmp[12:0]; + 14: sticky = |adj_op_tmp[13:0]; + 15: sticky = |adj_op_tmp[14:0]; + 16: sticky = |adj_op_tmp[15:0]; + 17: sticky = |adj_op_tmp[16:0]; + 18: sticky = |adj_op_tmp[17:0]; + 19: sticky = |adj_op_tmp[18:0]; + 20: sticky = |adj_op_tmp[19:0]; + 21: sticky = |adj_op_tmp[20:0]; + 22: sticky = |adj_op_tmp[21:0]; + 23: sticky = |adj_op_tmp[22:0]; + 24: sticky = |adj_op_tmp[23:0]; + 25: sticky = |adj_op_tmp[24:0]; + 26: sticky = |adj_op_tmp[25:0]; + 27: sticky = |adj_op_tmp[26:0]; + endcase + +// --------------------------------------------------------------------- +// Select operands for add/sub (recover hidden bit) + +assign fracta_n = expa_lt_expb ? {~expa_dn, fracta, 3'b0} : adj_op_out; +assign fractb_n = expa_lt_expb ? adj_op_out : {~expb_dn, fractb, 3'b0}; + +// --------------------------------------------------------------------- +// Sort operands (for sub only) + +assign fractb_lt_fracta = fractb_n > fracta_n; // fractb is larger than fracta +assign fracta_s = fractb_lt_fracta ? fractb_n : fracta_n; +assign fractb_s = fractb_lt_fracta ? fracta_n : fractb_n; + +always @(posedge clk) + fracta_out <= #1 fracta_s; + +always @(posedge clk) + fractb_out <= #1 fractb_s; + +// --------------------------------------------------------------------- +// Determine sign for the output + +// sign: 0=Positive Number; 1=Negative Number +always @(signa or signb or add or fractb_lt_fracta) + case({signa, signb, add}) // synopsys full_case parallel_case + + // Add + 3'b0_0_1: sign_d = 0; + 3'b0_1_1: sign_d = fractb_lt_fracta; + 3'b1_0_1: sign_d = !fractb_lt_fracta; + 3'b1_1_1: sign_d = 1; + + // Sub + 3'b0_0_0: sign_d = fractb_lt_fracta; + 3'b0_1_0: sign_d = 0; + 3'b1_0_0: sign_d = 1; + 3'b1_1_0: sign_d = !fractb_lt_fracta; + endcase + +always @(posedge clk) + sign <= #1 sign_d; + +// Fix sign for ZERO result +always @(posedge clk) + signa_r <= #1 signa; + +always @(posedge clk) + signb_r <= #1 signb; + +always @(posedge clk) + add_r <= #1 add; + +always @(posedge clk) + result_zero_sign <= #1 ( add_r & signa_r & signb_r) | + (!add_r & signa_r & !signb_r) | + ( add_r & (signa_r | signb_r) & (rmode==3)) | + (!add_r & (signa_r == signb_r) & (rmode==3)); + +// Fix sign for NAN result +always @(posedge clk) + fracta_lt_fractb <= #1 fracta < fractb; + +always @(posedge clk) + fracta_eq_fractb <= #1 fracta == fractb; + +assign nan_sign1 = fracta_eq_fractb ? (signa_r & signb_r) : fracta_lt_fractb ? signb_r : signa_r; + +always @(posedge clk) + nan_sign <= #1 (opa_nan & opb_nan) ? nan_sign1 : opb_nan ? signb_r : signa_r; + +//////////////////////////////////////////////////////////////////////// +// +// Decode Add/Sub operation +// + +// add: 1=Add; 0=Subtract +always @(signa or signb or add) + case({signa, signb, add}) // synopsys full_case parallel_case + + // Add + 3'b0_0_1: add_d = 1; + 3'b0_1_1: add_d = 0; + 3'b1_0_1: add_d = 0; + 3'b1_1_1: add_d = 1; + + // Sub + 3'b0_0_0: add_d = 0; + 3'b0_1_0: add_d = 1; + 3'b1_0_0: add_d = 1; + 3'b1_1_0: add_d = 0; + endcase + +always @(posedge clk) + fasu_op <= #1 add_d; + +endmodule diff --git a/tests/iwls2005/fpu/pre_norm_fmul.v b/tests/iwls2005/fpu/pre_norm_fmul.v new file mode 100644 index 000000000..26ddfeb75 --- /dev/null +++ b/tests/iwls2005/fpu/pre_norm_fmul.v @@ -0,0 +1,150 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Pre Normalize //// +//// Floating Point Pre Normalization Unit for FMUL //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +`timescale 1ns / 100ps + +module pre_norm_fmul(clk, fpu_op, opa, opb, fracta, fractb, exp_out, sign, + sign_exe, inf, exp_ovf, underflow); +input clk; +input [2:0] fpu_op; +input [31:0] opa, opb; +output [23:0] fracta, fractb; +output [7:0] exp_out; +output sign, sign_exe; +output inf; +output [1:0] exp_ovf; +output [2:0] underflow; + +//////////////////////////////////////////////////////////////////////// +// +// Local Wires and registers +// + +reg [7:0] exp_out; +wire signa, signb; +reg sign, sign_d; +reg sign_exe; +reg inf; +wire [1:0] exp_ovf_d; +reg [1:0] exp_ovf; +wire [7:0] expa, expb; +wire [7:0] exp_tmp1, exp_tmp2; +wire co1, co2; +wire expa_dn, expb_dn; +wire [7:0] exp_out_a; +wire opa_00, opb_00, fracta_00, fractb_00; +wire [7:0] exp_tmp3, exp_tmp4, exp_tmp5; +wire [2:0] underflow_d; +reg [2:0] underflow; +wire op_div = (fpu_op == 3'b011); +wire [7:0] exp_out_mul, exp_out_div; + +//////////////////////////////////////////////////////////////////////// +// +// Aliases +// + +assign signa = opa[31]; +assign signb = opb[31]; +assign expa = opa[30:23]; +assign expb = opb[30:23]; + +//////////////////////////////////////////////////////////////////////// +// +// Calculate Exponenet +// + +assign expa_dn = !(|expa); +assign expb_dn = !(|expb); +assign opa_00 = !(|opa[30:0]); +assign opb_00 = !(|opb[30:0]); +assign fracta_00 = !(|opa[22:0]); +assign fractb_00 = !(|opb[22:0]); + +assign fracta = {!expa_dn,opa[22:0]}; // Recover hidden bit +assign fractb = {!expb_dn,opb[22:0]}; // Recover hidden bit + +assign {co1,exp_tmp1} = op_div ? (expa - expb) : (expa + expb); +assign {co2,exp_tmp2} = op_div ? ({co1,exp_tmp1} + 8'h7f) : ({co1,exp_tmp1} - 8'h7f); + +assign exp_tmp3 = exp_tmp2 + 1; +assign exp_tmp4 = 8'h7f - exp_tmp1; +assign exp_tmp5 = op_div ? (exp_tmp4+1) : (exp_tmp4-1); + + +always@(posedge clk) + exp_out <= #1 op_div ? exp_out_div : exp_out_mul; + +assign exp_out_div = (expa_dn | expb_dn) ? (co2 ? exp_tmp5 : exp_tmp3 ) : co2 ? exp_tmp4 : exp_tmp2; +assign exp_out_mul = exp_ovf_d[1] ? exp_out_a : (expa_dn | expb_dn) ? exp_tmp3 : exp_tmp2; +assign exp_out_a = (expa_dn | expb_dn) ? exp_tmp5 : exp_tmp4; +assign exp_ovf_d[0] = op_div ? (expa[7] & !expb[7]) : (co2 & expa[7] & expb[7]); +assign exp_ovf_d[1] = op_div ? co2 : ((!expa[7] & !expb[7] & exp_tmp2[7]) | co2); + +always @(posedge clk) + exp_ovf <= #1 exp_ovf_d; + +assign underflow_d[0] = (exp_tmp1 < 8'h7f) & !co1 & !(opa_00 | opb_00 | expa_dn | expb_dn); +assign underflow_d[1] = ((expa[7] | expb[7]) & !opa_00 & !opb_00) | + (expa_dn & !fracta_00) | (expb_dn & !fractb_00); +assign underflow_d[2] = !opa_00 & !opb_00 & (exp_tmp1 == 8'h7f); + +always @(posedge clk) + underflow <= #1 underflow_d; + +always @(posedge clk) + inf <= #1 op_div ? (expb_dn & !expa[7]) : ({co1,exp_tmp1} > 9'h17e) ; + + +//////////////////////////////////////////////////////////////////////// +// +// Determine sign for the output +// + +// sign: 0=Posetive Number; 1=Negative Number +always @(signa or signb) + case({signa, signb}) // synopsys full_case parallel_case + 2'b0_0: sign_d = 0; + 2'b0_1: sign_d = 1; + 2'b1_0: sign_d = 1; + 2'b1_1: sign_d = 0; + endcase + +always @(posedge clk) + sign <= #1 sign_d; + +always @(posedge clk) + sign_exe <= #1 signa & signb; + +endmodule \ No newline at end of file diff --git a/tests/iwls2005/fpu/primitives.v b/tests/iwls2005/fpu/primitives.v new file mode 100644 index 000000000..2e7f050e5 --- /dev/null +++ b/tests/iwls2005/fpu/primitives.v @@ -0,0 +1,103 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Primitives //// +//// FPU Primitives //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + + +`timescale 1ns / 100ps + + +//////////////////////////////////////////////////////////////////////// +// +// Add/Sub +// + +module add_sub27(add, opa, opb, sum, co); +input add; +input [26:0] opa, opb; +output [26:0] sum; +output co; + + + +assign {co, sum} = add ? (opa + opb) : (opa - opb); + +endmodule + +//////////////////////////////////////////////////////////////////////// +// +// Multiply +// + +module mul_r2(clk, opa, opb, prod); +input clk; +input [23:0] opa, opb; +output [47:0] prod; + +reg [47:0] prod1, prod; + +always @(posedge clk) + prod1 <= #1 opa * opb; + +always @(posedge clk) + prod <= #1 prod1; + +endmodule + +//////////////////////////////////////////////////////////////////////// +// +// Divide +// + +module div_r2(clk, opa, opb, quo, rem); +input clk; +input [49:0] opa; +input [23:0] opb; +output [49:0] quo, rem; + +reg [49:0] quo, rem, quo1, remainder; + +always @(posedge clk) + quo1 <= #1 opa / opb; + +always @(posedge clk) + quo <= #1 quo1; + +always @(posedge clk) + remainder <= #1 opa % opb; + +always @(posedge clk) + rem <= #1 remainder; + +endmodule + + diff --git a/tests/iwls2005/i2c/i2c_master_bit_ctrl.v b/tests/iwls2005/i2c/i2c_master_bit_ctrl.v new file mode 100644 index 000000000..17b2c8b1f --- /dev/null +++ b/tests/iwls2005/i2c/i2c_master_bit_ctrl.v @@ -0,0 +1,535 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master bit-controller //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_bit_ctrl.v,v 1.11 2004/05/07 11:02:26 rherveille Exp $ +// +// $Date: 2004/05/07 11:02:26 $ +// $Revision: 1.11 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: i2c_master_bit_ctrl.v,v $ +// Revision 1.11 2004/05/07 11:02:26 rherveille +// Fixed a bug where the core would signal an arbitration lost (AL bit set), when another master controls the bus and the other master generates a STOP bit. +// +// Revision 1.10 2003/08/09 07:01:33 rherveille +// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. +// Fixed a potential bug in the byte controller's host-acknowledge generation. +// +// Revision 1.9 2003/03/10 14:26:37 rherveille +// Fixed cmd_ack generation item (no bug). +// +// Revision 1.8 2003/02/05 00:06:10 rherveille +// Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles. +// +// Revision 1.7 2002/12/26 16:05:12 rherveille +// Small code simplifications +// +// Revision 1.6 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.5 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.4 2002/10/30 18:10:07 rherveille +// Fixed some reported minor start/stop generation timing issuess. +// +// Revision 1.3 2002/06/15 07:37:03 rherveille +// Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment. +// +// Revision 1.2 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + +// +///////////////////////////////////// +// Bit controller section +///////////////////////////////////// +// +// Translate simple commands into SCL/SDA transitions +// Each command has 5 states, A/B/C/D/idle +// +// start: SCL ~~~~~~~~~~\____ +// SDA ~~~~~~~~\______ +// x | A | B | C | D | i +// +// repstart SCL ____/~~~~\___ +// SDA __/~~~\______ +// x | A | B | C | D | i +// +// stop SCL ____/~~~~~~~~ +// SDA ==\____/~~~~~ +// x | A | B | C | D | i +// +//- write SCL ____/~~~~\____ +// SDA ==X=========X= +// x | A | B | C | D | i +// +//- read SCL ____/~~~~\____ +// SDA XXXX=====XXXX +// x | A | B | C | D | i +// + +// Timing: Normal mode Fast mode +/////////////////////////////////////////////////////////////////////// +// Fscl 100KHz 400KHz +// Th_scl 4.0us 0.6us High period of SCL +// Tl_scl 4.7us 1.3us Low period of SCL +// Tsu:sta 4.7us 0.6us setup time for a repeated start condition +// Tsu:sto 4.0us 0.6us setup time for a stop conditon +// Tbuf 4.7us 1.3us Bus free time between a stop and start condition +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +`include "i2c_master_defines.v" + +module i2c_master_bit_ctrl( + clk, rst, nReset, + clk_cnt, ena, cmd, cmd_ack, busy, al, din, dout, + scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen + ); + + // + // inputs & outputs + // + input clk; + input rst; + input nReset; + input ena; // core enable signal + + input [15:0] clk_cnt; // clock prescale value + + input [3:0] cmd; + output cmd_ack; // command complete acknowledge + reg cmd_ack; + output busy; // i2c bus busy + reg busy; + output al; // i2c bus arbitration lost + reg al; + + input din; + output dout; + reg dout; + + // I2C lines + input scl_i; // i2c clock line input + output scl_o; // i2c clock line output + output scl_oen; // i2c clock line output enable (active low) + reg scl_oen; + input sda_i; // i2c data line input + output sda_o; // i2c data line output + output sda_oen; // i2c data line output enable (active low) + reg sda_oen; + + + // + // variable declarations + // + + reg sSCL, sSDA; // synchronized SCL and SDA inputs + reg dscl_oen; // delayed scl_oen + reg sda_chk; // check SDA output (Multi-master arbitration) + reg clk_en; // clock generation signals + wire slave_wait; +// reg [15:0] cnt = clk_cnt; // clock divider counter (simulation) + reg [15:0] cnt; // clock divider counter (synthesis) + + // state machine variable + reg [16:0] c_state; // synopsys enum_state + + // + // module body + // + + // whenever the slave is not ready it can delay the cycle by pulling SCL low + // delay scl_oen + always @(posedge clk) + dscl_oen <= #1 scl_oen; + + assign slave_wait = dscl_oen && !sSCL; + + + // generate clk enable signal + always @(posedge clk or negedge nReset) + if(~nReset) + begin + cnt <= #1 16'h0; + clk_en <= #1 1'b1; + end + else if (rst) + begin + cnt <= #1 16'h0; + clk_en <= #1 1'b1; + end + else if ( ~|cnt || ~ena) + if (~slave_wait) + begin + cnt <= #1 clk_cnt; + clk_en <= #1 1'b1; + end + else + begin + cnt <= #1 cnt; + clk_en <= #1 1'b0; + end + else + begin + cnt <= #1 cnt - 16'h1; + clk_en <= #1 1'b0; + end + + + // generate bus status controller + reg dSCL, dSDA; + reg sta_condition; + reg sto_condition; + + // synchronize SCL and SDA inputs + // reduce metastability risc + always @(posedge clk or negedge nReset) + if (~nReset) + begin + sSCL <= #1 1'b1; + sSDA <= #1 1'b1; + + dSCL <= #1 1'b1; + dSDA <= #1 1'b1; + end + else if (rst) + begin + sSCL <= #1 1'b1; + sSDA <= #1 1'b1; + + dSCL <= #1 1'b1; + dSDA <= #1 1'b1; + end + else + begin + sSCL <= #1 scl_i; + sSDA <= #1 sda_i; + + dSCL <= #1 sSCL; + dSDA <= #1 sSDA; + end + + // detect start condition => detect falling edge on SDA while SCL is high + // detect stop condition => detect rising edge on SDA while SCL is high + always @(posedge clk or negedge nReset) + if (~nReset) + begin + sta_condition <= #1 1'b0; + sto_condition <= #1 1'b0; + end + else if (rst) + begin + sta_condition <= #1 1'b0; + sto_condition <= #1 1'b0; + end + else + begin + sta_condition <= #1 ~sSDA & dSDA & sSCL; + sto_condition <= #1 sSDA & ~dSDA & sSCL; + end + + // generate i2c bus busy signal + always @(posedge clk or negedge nReset) + if(!nReset) + busy <= #1 1'b0; + else if (rst) + busy <= #1 1'b0; + else + busy <= #1 (sta_condition | busy) & ~sto_condition; + + // generate arbitration lost signal + // aribitration lost when: + // 1) master drives SDA high, but the i2c bus is low + // 2) stop detected while not requested + reg cmd_stop; + always @(posedge clk or negedge nReset) + if (~nReset) + cmd_stop <= #1 1'b0; + else if (rst) + cmd_stop <= #1 1'b0; + else if (clk_en) + cmd_stop <= #1 cmd == `I2C_CMD_STOP; + + always @(posedge clk or negedge nReset) + if (~nReset) + al <= #1 1'b0; + else if (rst) + al <= #1 1'b0; + else + al <= #1 (sda_chk & ~sSDA & sda_oen) | (|c_state & sto_condition & ~cmd_stop); + + + // generate dout signal (store SDA on rising edge of SCL) + always @(posedge clk) + if(sSCL & ~dSCL) + dout <= #1 sSDA; + + // generate statemachine + + // nxt_state decoder + parameter [16:0] idle = 17'b0_0000_0000_0000_0000; + parameter [16:0] start_a = 17'b0_0000_0000_0000_0001; + parameter [16:0] start_b = 17'b0_0000_0000_0000_0010; + parameter [16:0] start_c = 17'b0_0000_0000_0000_0100; + parameter [16:0] start_d = 17'b0_0000_0000_0000_1000; + parameter [16:0] start_e = 17'b0_0000_0000_0001_0000; + parameter [16:0] stop_a = 17'b0_0000_0000_0010_0000; + parameter [16:0] stop_b = 17'b0_0000_0000_0100_0000; + parameter [16:0] stop_c = 17'b0_0000_0000_1000_0000; + parameter [16:0] stop_d = 17'b0_0000_0001_0000_0000; + parameter [16:0] rd_a = 17'b0_0000_0010_0000_0000; + parameter [16:0] rd_b = 17'b0_0000_0100_0000_0000; + parameter [16:0] rd_c = 17'b0_0000_1000_0000_0000; + parameter [16:0] rd_d = 17'b0_0001_0000_0000_0000; + parameter [16:0] wr_a = 17'b0_0010_0000_0000_0000; + parameter [16:0] wr_b = 17'b0_0100_0000_0000_0000; + parameter [16:0] wr_c = 17'b0_1000_0000_0000_0000; + parameter [16:0] wr_d = 17'b1_0000_0000_0000_0000; + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b0; + scl_oen <= #1 1'b1; + sda_oen <= #1 1'b1; + sda_chk <= #1 1'b0; + end + else if (rst | al) + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b0; + scl_oen <= #1 1'b1; + sda_oen <= #1 1'b1; + sda_chk <= #1 1'b0; + end + else + begin + cmd_ack <= #1 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle + + if (clk_en) + case (c_state) // synopsys full_case parallel_case + // idle state + idle: + begin + case (cmd) // synopsys full_case parallel_case + `I2C_CMD_START: + c_state <= #1 start_a; + + `I2C_CMD_STOP: + c_state <= #1 stop_a; + + `I2C_CMD_WRITE: + c_state <= #1 wr_a; + + `I2C_CMD_READ: + c_state <= #1 rd_a; + + default: + c_state <= #1 idle; + endcase + + scl_oen <= #1 scl_oen; // keep SCL in same state + sda_oen <= #1 sda_oen; // keep SDA in same state + sda_chk <= #1 1'b0; // don't check SDA output + end + + // start + start_a: + begin + c_state <= #1 start_b; + scl_oen <= #1 scl_oen; // keep SCL in same state + sda_oen <= #1 1'b1; // set SDA high + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_b: + begin + c_state <= #1 start_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b1; // keep SDA high + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_c: + begin + c_state <= #1 start_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // set SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_d: + begin + c_state <= #1 start_e; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + start_e: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + // stop + stop_a: + begin + c_state <= #1 stop_b; + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 1'b0; // set SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + stop_b: + begin + c_state <= #1 stop_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + stop_c: + begin + c_state <= #1 stop_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // keep SDA low + sda_chk <= #1 1'b0; // don't check SDA output + end + + stop_d: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b1; // set SDA high + sda_chk <= #1 1'b0; // don't check SDA output + end + + // read + rd_a: + begin + c_state <= #1 rd_b; + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 1'b1; // tri-state SDA + sda_chk <= #1 1'b0; // don't check SDA output + end + + rd_b: + begin + c_state <= #1 rd_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b1; // keep SDA tri-stated + sda_chk <= #1 1'b0; // don't check SDA output + end + + rd_c: + begin + c_state <= #1 rd_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b1; // keep SDA tri-stated + sda_chk <= #1 1'b0; // don't check SDA output + end + + rd_d: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 1'b1; // keep SDA tri-stated + sda_chk <= #1 1'b0; // don't check SDA output + end + + // write + wr_a: + begin + c_state <= #1 wr_b; + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 din; // set SDA + sda_chk <= #1 1'b0; // don't check SDA output (SCL low) + end + + wr_b: + begin + c_state <= #1 wr_c; + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 din; // keep SDA + sda_chk <= #1 1'b1; // check SDA output + end + + wr_c: + begin + c_state <= #1 wr_d; + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 din; + sda_chk <= #1 1'b1; // check SDA output + end + + wr_d: + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b1; + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 din; + sda_chk <= #1 1'b0; // don't check SDA output (SCL low) + end + + endcase + end + + + // assign scl and sda output (always gnd) + assign scl_o = 1'b0; + assign sda_o = 1'b0; + +endmodule diff --git a/tests/iwls2005/i2c/i2c_master_byte_ctrl.v b/tests/iwls2005/i2c/i2c_master_byte_ctrl.v new file mode 100644 index 000000000..d091d1e36 --- /dev/null +++ b/tests/iwls2005/i2c/i2c_master_byte_ctrl.v @@ -0,0 +1,344 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master byte-controller //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_byte_ctrl.v,v 1.7 2004/02/18 11:40:46 rherveille Exp $ +// +// $Date: 2004/02/18 11:40:46 $ +// $Revision: 1.7 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: i2c_master_byte_ctrl.v,v $ +// Revision 1.7 2004/02/18 11:40:46 rherveille +// Fixed a potential bug in the statemachine. During a 'stop' 2 cmd_ack signals were generated. Possibly canceling a new start command. +// +// Revision 1.6 2003/08/09 07:01:33 rherveille +// Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. +// Fixed a potential bug in the byte controller's host-acknowledge generation. +// +// Revision 1.5 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.4 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.3 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +`include "i2c_master_defines.v" + +module i2c_master_byte_ctrl ( + clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din, + cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); + + // + // inputs & outputs + // + input clk; // master clock + input rst; // synchronous active high reset + input nReset; // asynchronous active low reset + input ena; // core enable signal + + input [15:0] clk_cnt; // 4x SCL + + // control inputs + input start; + input stop; + input read; + input write; + input ack_in; + input [7:0] din; + + // status outputs + output cmd_ack; + reg cmd_ack; + output ack_out; + reg ack_out; + output i2c_busy; + output i2c_al; + output [7:0] dout; + + // I2C signals + input scl_i; + output scl_o; + output scl_oen; + input sda_i; + output sda_o; + output sda_oen; + + + // + // Variable declarations + // + + // statemachine + parameter [4:0] ST_IDLE = 5'b0_0000; + parameter [4:0] ST_START = 5'b0_0001; + parameter [4:0] ST_READ = 5'b0_0010; + parameter [4:0] ST_WRITE = 5'b0_0100; + parameter [4:0] ST_ACK = 5'b0_1000; + parameter [4:0] ST_STOP = 5'b1_0000; + + // signals for bit_controller + reg [3:0] core_cmd; + reg core_txd; + wire core_ack, core_rxd; + + // signals for shift register + reg [7:0] sr; //8bit shift register + reg shift, ld; + + // signals for state machine + wire go; + reg [2:0] dcnt; + wire cnt_done; + + // + // Module body + // + + // hookup bit_controller + i2c_master_bit_ctrl bit_controller ( + .clk ( clk ), + .rst ( rst ), + .nReset ( nReset ), + .ena ( ena ), + .clk_cnt ( clk_cnt ), + .cmd ( core_cmd ), + .cmd_ack ( core_ack ), + .busy ( i2c_busy ), + .al ( i2c_al ), + .din ( core_txd ), + .dout ( core_rxd ), + .scl_i ( scl_i ), + .scl_o ( scl_o ), + .scl_oen ( scl_oen ), + .sda_i ( sda_i ), + .sda_o ( sda_o ), + .sda_oen ( sda_oen ) + ); + + // generate go-signal + assign go = (read | write | stop) & ~cmd_ack; + + // assign dout output to shift-register + assign dout = sr; + + // generate shift register + always @(posedge clk or negedge nReset) + if (!nReset) + sr <= #1 8'h0; + else if (rst) + sr <= #1 8'h0; + else if (ld) + sr <= #1 din; + else if (shift) + sr <= #1 {sr[6:0], core_rxd}; + + // generate counter + always @(posedge clk or negedge nReset) + if (!nReset) + dcnt <= #1 3'h0; + else if (rst) + dcnt <= #1 3'h0; + else if (ld) + dcnt <= #1 3'h7; + else if (shift) + dcnt <= #1 dcnt - 3'h1; + + assign cnt_done = ~(|dcnt); + + // + // state machine + // + reg [4:0] c_state; // synopsis enum_state + + always @(posedge clk or negedge nReset) + if (!nReset) + begin + core_cmd <= #1 `I2C_CMD_NOP; + core_txd <= #1 1'b0; + shift <= #1 1'b0; + ld <= #1 1'b0; + cmd_ack <= #1 1'b0; + c_state <= #1 ST_IDLE; + ack_out <= #1 1'b0; + end + else if (rst | i2c_al) + begin + core_cmd <= #1 `I2C_CMD_NOP; + core_txd <= #1 1'b0; + shift <= #1 1'b0; + ld <= #1 1'b0; + cmd_ack <= #1 1'b0; + c_state <= #1 ST_IDLE; + ack_out <= #1 1'b0; + end + else + begin + // initially reset all signals + core_txd <= #1 sr[7]; + shift <= #1 1'b0; + ld <= #1 1'b0; + cmd_ack <= #1 1'b0; + + case (c_state) // synopsys full_case parallel_case + ST_IDLE: + if (go) + begin + if (start) + begin + c_state <= #1 ST_START; + core_cmd <= #1 `I2C_CMD_START; + end + else if (read) + begin + c_state <= #1 ST_READ; + core_cmd <= #1 `I2C_CMD_READ; + end + else if (write) + begin + c_state <= #1 ST_WRITE; + core_cmd <= #1 `I2C_CMD_WRITE; + end + else // stop + begin + c_state <= #1 ST_STOP; + core_cmd <= #1 `I2C_CMD_STOP; + end + + ld <= #1 1'b1; + end + + ST_START: + if (core_ack) + begin + if (read) + begin + c_state <= #1 ST_READ; + core_cmd <= #1 `I2C_CMD_READ; + end + else + begin + c_state <= #1 ST_WRITE; + core_cmd <= #1 `I2C_CMD_WRITE; + end + + ld <= #1 1'b1; + end + + ST_WRITE: + if (core_ack) + if (cnt_done) + begin + c_state <= #1 ST_ACK; + core_cmd <= #1 `I2C_CMD_READ; + end + else + begin + c_state <= #1 ST_WRITE; // stay in same state + core_cmd <= #1 `I2C_CMD_WRITE; // write next bit + shift <= #1 1'b1; + end + + ST_READ: + if (core_ack) + begin + if (cnt_done) + begin + c_state <= #1 ST_ACK; + core_cmd <= #1 `I2C_CMD_WRITE; + end + else + begin + c_state <= #1 ST_READ; // stay in same state + core_cmd <= #1 `I2C_CMD_READ; // read next bit + end + + shift <= #1 1'b1; + core_txd <= #1 ack_in; + end + + ST_ACK: + if (core_ack) + begin + if (stop) + begin + c_state <= #1 ST_STOP; + core_cmd <= #1 `I2C_CMD_STOP; + end + else + begin + c_state <= #1 ST_IDLE; + core_cmd <= #1 `I2C_CMD_NOP; + + // generate command acknowledge signal + cmd_ack <= #1 1'b1; + end + + // assign ack_out output to bit_controller_rxd (contains last received bit) + ack_out <= #1 core_rxd; + + core_txd <= #1 1'b1; + end + else + core_txd <= #1 ack_in; + + ST_STOP: + if (core_ack) + begin + c_state <= #1 ST_IDLE; + core_cmd <= #1 `I2C_CMD_NOP; + + // generate command acknowledge signal + cmd_ack <= #1 1'b1; + end + + endcase + end +endmodule diff --git a/tests/iwls2005/i2c/i2c_master_defines.v b/tests/iwls2005/i2c/i2c_master_defines.v new file mode 100644 index 000000000..ee3b694fa --- /dev/null +++ b/tests/iwls2005/i2c/i2c_master_defines.v @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant I2C Master controller defines //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_defines.v,v 1.3 2001/11/05 11:59:25 rherveille Exp $ +// +// $Date: 2001/11/05 11:59:25 $ +// $Revision: 1.3 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: i2c_master_defines.v,v $ +// Revision 1.3 2001/11/05 11:59:25 rherveille +// Fixed wb_ack_o generation bug. +// Fixed bug in the byte_controller statemachine. +// Added headers. +// + + +// I2C registers wishbone addresses + +// bitcontroller states +`define I2C_CMD_NOP 4'b0000 +`define I2C_CMD_START 4'b0001 +`define I2C_CMD_STOP 4'b0010 +`define I2C_CMD_WRITE 4'b0100 +`define I2C_CMD_READ 4'b1000 diff --git a/tests/iwls2005/i2c/i2c_master_top.v b/tests/iwls2005/i2c/i2c_master_top.v new file mode 100644 index 000000000..30689bd70 --- /dev/null +++ b/tests/iwls2005/i2c/i2c_master_top.v @@ -0,0 +1,301 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE revB.2 compliant I2C Master controller Top-level //// +//// //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_master_top.v,v 1.11 2005/02/27 09:26:24 rherveille Exp $ +// +// $Date: 2005/02/27 09:26:24 $ +// $Revision: 1.11 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: i2c_master_top.v,v $ +// Revision 1.11 2005/02/27 09:26:24 rherveille +// Fixed register overwrite issue. +// Removed full_case pragma, replaced it by a default statement. +// +// Revision 1.10 2003/09/01 10:34:38 rherveille +// Fix a blocking vs. non-blocking error in the wb_dat output mux. +// +// Revision 1.9 2003/01/09 16:44:45 rherveille +// Fixed a bug in the Command Register declaration. +// +// Revision 1.8 2002/12/26 16:05:12 rherveille +// Small code simplifications +// +// Revision 1.7 2002/12/26 15:02:32 rherveille +// Core is now a Multimaster I2C controller +// +// Revision 1.6 2002/11/30 22:24:40 rherveille +// Cleaned up code +// +// Revision 1.5 2001/11/10 10:52:55 rherveille +// Changed PRER reset value from 0x0000 to 0xffff, conform specs. +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +`include "i2c_master_defines.v" + +module i2c_master_top( + wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o, + wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o, + scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o ); + + // parameters + parameter ARST_LVL = 1'b0; // asynchronous reset level + + // + // inputs & outputs + // + + // wishbone signals + input wb_clk_i; // master clock input + input wb_rst_i; // synchronous active high reset + input arst_i; // asynchronous reset + input [2:0] wb_adr_i; // lower address bits + input [7:0] wb_dat_i; // databus input + output [7:0] wb_dat_o; // databus output + input wb_we_i; // write enable input + input wb_stb_i; // stobe/core select signal + input wb_cyc_i; // valid bus cycle input + output wb_ack_o; // bus cycle acknowledge output + output wb_inta_o; // interrupt request signal output + + reg [7:0] wb_dat_o; + reg wb_ack_o; + reg wb_inta_o; + + // I2C signals + // i2c clock line + input scl_pad_i; // SCL-line input + output scl_pad_o; // SCL-line output (always 1'b0) + output scl_padoen_o; // SCL-line output enable (active low) + + // i2c data line + input sda_pad_i; // SDA-line input + output sda_pad_o; // SDA-line output (always 1'b0) + output sda_padoen_o; // SDA-line output enable (active low) + + + // + // variable declarations + // + + // registers + reg [15:0] prer; // clock prescale register + reg [ 7:0] ctr; // control register + reg [ 7:0] txr; // transmit register + wire [ 7:0] rxr; // receive register + reg [ 7:0] cr; // command register + wire [ 7:0] sr; // status register + + // done signal: command completed, clear command register + wire done; + + // core enable signal + wire core_en; + wire ien; + + // status register signals + wire irxack; + reg rxack; // received aknowledge from slave + reg tip; // transfer in progress + reg irq_flag; // interrupt pending flag + wire i2c_busy; // bus busy (start signal detected) + wire i2c_al; // i2c bus arbitration lost + reg al; // status register arbitration lost bit + + // + // module body + // + + // generate internal reset + wire rst_i = arst_i ^ ARST_LVL; + + // generate wishbone signals + wire wb_wacc = wb_cyc_i & wb_stb_i & wb_we_i; + + // generate acknowledge output signal + always @(posedge wb_clk_i) + wb_ack_o <= #1 wb_cyc_i & wb_stb_i & ~wb_ack_o; // because timing is always honored + + // assign DAT_O + always @(posedge wb_clk_i) + begin + case (wb_adr_i) // synopsis parallel_case + 3'b000: wb_dat_o <= #1 prer[ 7:0]; + 3'b001: wb_dat_o <= #1 prer[15:8]; + 3'b010: wb_dat_o <= #1 ctr; + 3'b011: wb_dat_o <= #1 rxr; // write is transmit register (txr) + 3'b100: wb_dat_o <= #1 sr; // write is command register (cr) + 3'b101: wb_dat_o <= #1 txr; + 3'b110: wb_dat_o <= #1 cr; + 3'b111: wb_dat_o <= #1 0; // reserved + endcase + end + + // generate registers + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + prer <= #1 16'hffff; + ctr <= #1 8'h0; + txr <= #1 8'h0; + end + else if (wb_rst_i) + begin + prer <= #1 16'hffff; + ctr <= #1 8'h0; + txr <= #1 8'h0; + end + else + if (wb_wacc) + case (wb_adr_i) // synopsis parallel_case + 3'b000 : prer [ 7:0] <= #1 wb_dat_i; + 3'b001 : prer [15:8] <= #1 wb_dat_i; + 3'b010 : ctr <= #1 wb_dat_i; + 3'b011 : txr <= #1 wb_dat_i; + default: ; + endcase + + // generate command register (special case) + always @(posedge wb_clk_i or negedge rst_i) + if (~rst_i) + cr <= #1 8'h0; + else if (wb_rst_i) + cr <= #1 8'h0; + else if (wb_wacc) + begin + if (core_en & (wb_adr_i == 3'b100) ) + cr <= #1 wb_dat_i; + end + else + begin + if (done | i2c_al) + cr[7:4] <= #1 4'h0; // clear command bits when done + // or when aribitration lost + cr[2:1] <= #1 2'b0; // reserved bits + cr[0] <= #1 2'b0; // clear IRQ_ACK bit + end + + + // decode command register + wire sta = cr[7]; + wire sto = cr[6]; + wire rd = cr[5]; + wire wr = cr[4]; + wire ack = cr[3]; + wire iack = cr[0]; + + // decode control register + assign core_en = ctr[7]; + assign ien = ctr[6]; + + // hookup byte controller block + i2c_master_byte_ctrl byte_controller ( + .clk ( wb_clk_i ), + .rst ( wb_rst_i ), + .nReset ( rst_i ), + .ena ( core_en ), + .clk_cnt ( prer ), + .start ( sta ), + .stop ( sto ), + .read ( rd ), + .write ( wr ), + .ack_in ( ack ), + .din ( txr ), + .cmd_ack ( done ), + .ack_out ( irxack ), + .dout ( rxr ), + .i2c_busy ( i2c_busy ), + .i2c_al ( i2c_al ), + .scl_i ( scl_pad_i ), + .scl_o ( scl_pad_o ), + .scl_oen ( scl_padoen_o ), + .sda_i ( sda_pad_i ), + .sda_o ( sda_pad_o ), + .sda_oen ( sda_padoen_o ) + ); + + // status register block + interrupt request signal + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + al <= #1 1'b0; + rxack <= #1 1'b0; + tip <= #1 1'b0; + irq_flag <= #1 1'b0; + end + else if (wb_rst_i) + begin + al <= #1 1'b0; + rxack <= #1 1'b0; + tip <= #1 1'b0; + irq_flag <= #1 1'b0; + end + else + begin + al <= #1 i2c_al | (al & ~sta); + rxack <= #1 irxack; + tip <= #1 (rd | wr); + irq_flag <= #1 (done | i2c_al | irq_flag) & ~iack; // interrupt request flag is always generated + end + + // generate interrupt request signals + always @(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + wb_inta_o <= #1 1'b0; + else if (wb_rst_i) + wb_inta_o <= #1 1'b0; + else + wb_inta_o <= #1 irq_flag && ien; // interrupt signal is only generated when IEN (interrupt enable bit is set) + + // assign status register bits + assign sr[7] = rxack; + assign sr[6] = i2c_busy; + assign sr[5] = al; + assign sr[4:2] = 3'h0; // reserved + assign sr[1] = tip; + assign sr[0] = irq_flag; + +endmodule diff --git a/tests/iwls2005/i2c/timescale.v b/tests/iwls2005/i2c/timescale.v new file mode 100644 index 000000000..60d4ecbd1 --- /dev/null +++ b/tests/iwls2005/i2c/timescale.v @@ -0,0 +1,2 @@ +`timescale 1ns / 10ps + diff --git a/tests/iwls2005/run-fm.sh b/tests/iwls2005/run-fm.sh new file mode 100755 index 000000000..14bb4e82c --- /dev/null +++ b/tests/iwls2005/run-fm.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +if [ -n "$REMOTE_YOSYS_ROOT" ]; then + rsync --exclude=".svn" --exclude="synth.log" --exclude="run-fm.sh" -rv -e "${REMOTE_YOSYS_SSH:-ssh}" "$REMOTE_YOSYS_ROOT"/tests/iwls2005/. . +fi + +exec_fm() +{ + dir=$1; top=$2; shift; shift + cat > $dir/fm.do <<- EOT + set hdlin_ignore_full_case false + set hdlin_warn_on_mismatch_message "FMR_ELAB-115 FMR_ELAB-146 FMR_ELAB-147" + read_verilog -container r -libname WORK -01 { $* } + set_top r:/WORK/$top + read_verilog -container i -libname WORK -01 synth.v + # read_verilog -container i -technology_library -libname TECH_WORK -01 ../../../techlibs/stdcells_sim.v + set_top i:/WORK/$top + if ![verify] start_gui exit + EOT + ( cd $dir; fm_shell -64 -file fm.do 2>&1 | tee fm.log; ) +} + +# cores that validated +exec_fm aes_core aes_cipher_top aes_cipher_top.v aes_inv_cipher_top.v aes_inv_sbox.v aes_key_expand_128.v aes_rcon.v aes_sbox.v +exec_fm i2c i2c_master_top i2c_master_top.v i2c_master_bit_ctrl.v i2c_master_byte_ctrl.v +exec_fm sasc sasc_top sasc_top.v sasc_brg.v sasc_fifo4.v +exec_fm simple_spi simple_spi_top simple_spi_top.v fifo4.v +exec_fm spi spi_top spi_top.v spi_clgen.v spi_shift.v +exec_fm ss_pcm pcm_slv_top pcm_slv_top.v +exec_fm systemcaes aes aes.v byte_mixcolum.v keysched.v mixcolum.v sbox.v subbytes.v word_mixcolum.v +exec_fm usb_phy usb_phy usb_phy.v usb_rx_phy.v usb_tx_phy.v + +# cores with known problems (the fpu core unfortunately was designed with logic loops) +#exec_fm fpu fpu fpu.v except.v post_norm.v pre_norm_fmul.v pre_norm.v primitives.v + +# summary +echo; echo +for x in */fm.log; do + echo -e "${x%/*}\\t$( egrep '^Verification (SUCCEEDED|FAILED)' $x; )" +done | expand -t15 +echo; echo + diff --git a/tests/iwls2005/run-synth.sh b/tests/iwls2005/run-synth.sh new file mode 100755 index 000000000..2f1e30661 --- /dev/null +++ b/tests/iwls2005/run-synth.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +make -C ../.. +set -x + +vg="" +# vg="valgrind --leak-check=full --show-reachable=yes --log-file=valgrind.log" + +cd aes_core +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + aes_cipher_top.v aes_inv_cipher_top.v aes_inv_sbox.v \ + aes_key_expand_128.v aes_rcon.v aes_sbox.v + +cd ../fpu +time $vg ../../../yosys -qt -l synth.log -o synth.v -f "verilog -nolatches" -s ../run-synth.ys \ + fpu.v except.v post_norm.v pre_norm_fmul.v pre_norm.v primitives.v + +cd ../i2c +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + i2c_master_top.v i2c_master_bit_ctrl.v i2c_master_byte_ctrl.v + +cd ../sasc +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + sasc_top.v sasc_brg.v sasc_fifo4.v + +cd ../simple_spi +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + simple_spi_top.v fifo4.v + +cd ../spi +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + spi_top.v spi_clgen.v spi_shift.v + +cd ../ss_pcm +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + pcm_slv_top.v + +cd ../systemcaes +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + aes.v byte_mixcolum.v keysched.v mixcolum.v sbox.v subbytes.v word_mixcolum.v + +cd ../usb_phy +time $vg ../../../yosys -qt -l synth.log -o synth.v -s ../run-synth.ys \ + usb_phy.v usb_rx_phy.v usb_tx_phy.v + diff --git a/tests/iwls2005/run-synth.ys b/tests/iwls2005/run-synth.ys new file mode 100644 index 000000000..f3253d5f9 --- /dev/null +++ b/tests/iwls2005/run-synth.ys @@ -0,0 +1,11 @@ +hierarchy +proc +opt +memory +opt +# fsm -norecode +# opt +techmap +opt +abc +opt diff --git a/tests/iwls2005/sasc/sasc_brg.v b/tests/iwls2005/sasc/sasc_brg.v new file mode 100644 index 000000000..74a7cc5b6 --- /dev/null +++ b/tests/iwls2005/sasc/sasc_brg.v @@ -0,0 +1,160 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Simple Baud Rate Generator //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/sasc/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: sasc_brg.v,v 1.2 2002/11/08 15:22:49 rudi Exp $ +// +// $Date: 2002/11/08 15:22:49 $ +// $Revision: 1.2 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: sasc_brg.v,v $ +// Revision 1.2 2002/11/08 15:22:49 rudi +// +// Fixed a typo in brg +// +// Revision 1.1.1.1 2002/09/16 16:16:40 rudi +// Initial Checkin +// +// +// +// +// +// +// +// + +`include "timescale.v" + +/* + Baud rate Generator + ================== + + div0 - is the first stage divider + Set this to the desired number of cycles less two + div1 - is the second stage divider + Set this to the actual number of cycles + + Remember you have to generate a baud rate that is 4 higher than what + you really want. This is because of the DPLL in the RX section ... + + Example: + If your system clock is 50MHz and you want to generate a 9.6 Kbps baud rate: + 9600*4 = 38400KHz + 50MHz/38400KHz=1302 or 6*217 + set div0=4 (6-2) and set div1=217 + +*/ + +module sasc_brg(clk, rst, div0, div1, sio_ce, sio_ce_x4); +input clk; +input rst; +input [7:0] div0, div1; +output sio_ce, sio_ce_x4; + +/////////////////////////////////////////////////////////////////// +// +// Local Wires and Registers +// + +reg [7:0] ps; +reg ps_clr; +reg [7:0] br_cnt; +reg br_clr; +reg sio_ce_x4_r; +reg [1:0] cnt; +reg sio_ce, sio_ce_x4; +reg sio_ce_r ; +reg sio_ce_x4_t; + +/////////////////////////////////////////////////////////////////// +// +// Boud Rate Generator +// + +// ----------------------------------------------------- +// Prescaler +always @(posedge clk) + if(!rst) ps <= #1 8'h0; + else + if(ps_clr) ps <= #1 8'h0; + else ps <= #1 ps + 8'h1; + +always @(posedge clk) + ps_clr <= #1 (ps == div0); // Desired number of cycles less 2 + +// ----------------------------------------------------- +// Oversampled Boud Rate (x4) +always @(posedge clk) + if(!rst) br_cnt <= #1 8'h0; + else + if(br_clr) br_cnt <= #1 8'h0; + else + if(ps_clr) br_cnt <= #1 br_cnt + 8'h1; + +always @(posedge clk) + br_clr <= #1 (br_cnt == div1); // Prciese number of PS cycles + +always @(posedge clk) + sio_ce_x4_r <= #1 br_clr; + +always @(posedge clk) + sio_ce_x4_t <= #1 !sio_ce_x4_r & br_clr; + +always @(posedge clk) + sio_ce_x4 <= #1 sio_ce_x4_t; + +// ----------------------------------------------------- +// Actual Boud rate +always @(posedge clk) + if(!rst) cnt <= #1 2'h0; + else + if(!sio_ce_x4_r & br_clr) cnt <= #1 cnt + 2'h1; + +always @(posedge clk) + sio_ce_r <= #1 (cnt == 2'h0); + +always @(posedge clk) + sio_ce <= #1 !sio_ce_r & (cnt == 2'h0); + +endmodule + diff --git a/tests/iwls2005/sasc/sasc_fifo4.v b/tests/iwls2005/sasc/sasc_fifo4.v new file mode 100644 index 000000000..ab9b9fefe --- /dev/null +++ b/tests/iwls2005/sasc/sasc_fifo4.v @@ -0,0 +1,135 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// FIFO 4 entries deep //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/sasc/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: sasc_fifo4.v,v 1.1.1.1 2002/09/16 16:16:41 rudi Exp $ +// +// $Date: 2002/09/16 16:16:41 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: sasc_fifo4.v,v $ +// Revision 1.1.1.1 2002/09/16 16:16:41 rudi +// Initial Checkin +// +// +// +// +// +// + +`include "timescale.v" + +// 4 entry deep fast fifo +module sasc_fifo4(clk, rst, clr, din, we, dout, re, full, empty); + +input clk, rst; +input clr; +input [7:0] din; +input we; +output [7:0] dout; +input re; +output full, empty; + + +//////////////////////////////////////////////////////////////////// +// +// Local Wires +// + +reg [7:0] mem[0:3]; +reg [1:0] wp; +reg [1:0] rp; +wire [1:0] wp_p1; +wire [1:0] wp_p2; +wire [1:0] rp_p1; +wire full, empty; +reg gb; + +//////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +always @(posedge clk or negedge rst) + if(!rst) wp <= #1 2'h0; + else + if(clr) wp <= #1 2'h0; + else + if(we) wp <= #1 wp_p1; + +assign wp_p1 = wp + 2'h1; +assign wp_p2 = wp + 2'h2; + +always @(posedge clk or negedge rst) + if(!rst) rp <= #1 2'h0; + else + if(clr) rp <= #1 2'h0; + else + if(re) rp <= #1 rp_p1; + +assign rp_p1 = rp + 2'h1; + +// Fifo Output +assign dout = mem[ rp ]; + +// Fifo Input +always @(posedge clk) + if(we) mem[ wp ] <= #1 din; + +// Status +assign empty = (wp == rp) & !gb; +assign full = (wp == rp) & gb; + +// Guard Bit ... +always @(posedge clk) + if(!rst) gb <= #1 1'b0; + else + if(clr) gb <= #1 1'b0; + else + if((wp_p1 == rp) & we) gb <= #1 1'b1; + else + if(re) gb <= #1 1'b0; + +endmodule + + diff --git a/tests/iwls2005/sasc/sasc_top.v b/tests/iwls2005/sasc/sasc_top.v new file mode 100644 index 000000000..a59329add --- /dev/null +++ b/tests/iwls2005/sasc/sasc_top.v @@ -0,0 +1,301 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// Simple Asynchronous Serial Comm. Device //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/sasc/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: sasc_top.v,v 1.1.1.1 2002/09/16 16:16:42 rudi Exp $ +// +// $Date: 2002/09/16 16:16:42 $ +// $Revision: 1.1.1.1 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: sasc_top.v,v $ +// Revision 1.1.1.1 2002/09/16 16:16:42 rudi +// Initial Checkin +// +// +// +// +// +// +// +// + +`include "timescale.v" + +/* +Serial IO Interface +=============================== +RTS I Request To Send +CTS O Clear to send +TD I Transmit Data +RD O Receive Data +*/ + +module sasc_top( clk, rst, + + // SIO + rxd_i, txd_o, cts_i, rts_o, + + // External Baud Rate Generator + sio_ce, sio_ce_x4, + + // Internal Interface + din_i, dout_o, re_i, we_i, full_o, empty_o); + +input clk; +input rst; +input rxd_i; +output txd_o; +input cts_i; +output rts_o; +input sio_ce; +input sio_ce_x4; +input [7:0] din_i; +output [7:0] dout_o; +input re_i, we_i; +output full_o, empty_o; + +/////////////////////////////////////////////////////////////////// +// +// Local Wires and Registers +// + +parameter START_BIT = 1'b0, + STOP_BIT = 1'b1, + IDLE_BIT = 1'b1; + +wire [7:0] txd_p; +reg load; +reg load_r; +wire load_e; +reg [9:0] hold_reg; +wire txf_empty; +reg txd_o; +reg shift_en; +reg [3:0] tx_bit_cnt; +reg rxd_s, rxd_r; +wire start; +reg [3:0] rx_bit_cnt; +reg rx_go; +reg [9:0] rxr; +reg rx_valid, rx_valid_r; +wire rx_we; +wire rxf_full; +reg rts_o; +reg txf_empty_r; +reg shift_en_r; +reg rxd_r1, rxd_r2; +wire lock_en; +reg change; +reg rx_sio_ce_d, rx_sio_ce_r1, rx_sio_ce_r2, rx_sio_ce; +reg [1:0] dpll_state, dpll_next_state; + +/////////////////////////////////////////////////////////////////// +// +// IO Fifo's +// + +sasc_fifo4 tx_fifo( .clk( clk ), + .rst( rst ), + .clr( 1'b0 ), + .din( din_i ), + .we( we_i ), + .dout( txd_p ), + .re( load_e ), + .full( full_o ), + .empty( txf_empty ) + ); + +sasc_fifo4 rx_fifo( .clk( clk ), + .rst( rst ), + .clr( 1'b0 ), + .din( rxr[9:2] ), + .we( rx_we ), + .dout( dout_o ), + .re( re_i ), + .full( rxf_full ), + .empty( empty_o ) + ); + +/////////////////////////////////////////////////////////////////// +// +// Transmit Logic +// +always @(posedge clk) + if(!rst) txf_empty_r <= #1 1'b1; + else + if(sio_ce) txf_empty_r <= #1 txf_empty; + +always @(posedge clk) + load <= #1 !txf_empty_r & !shift_en & !cts_i; + +always @(posedge clk) + load_r <= #1 load; + +assign load_e = load & sio_ce; + +always @(posedge clk) + if(load_e) hold_reg <= #1 {STOP_BIT, txd_p, START_BIT}; + else + if(shift_en & sio_ce) hold_reg <= #1 {IDLE_BIT, hold_reg[9:1]}; + +always @(posedge clk) + if(!rst) txd_o <= #1 IDLE_BIT; + else + if(sio_ce) + if(shift_en | shift_en_r) txd_o <= #1 hold_reg[0]; + else txd_o <= #1 IDLE_BIT; + +always @(posedge clk) + if(!rst) tx_bit_cnt <= #1 4'h9; + else + if(load_e) tx_bit_cnt <= #1 4'h0; + else + if(shift_en & sio_ce) tx_bit_cnt <= #1 tx_bit_cnt + 4'h1; + +always @(posedge clk) + shift_en <= #1 (tx_bit_cnt != 4'h9); + +always @(posedge clk) + if(!rst) shift_en_r <= #1 1'b0; + else + if(sio_ce) shift_en_r <= #1 shift_en; + +/////////////////////////////////////////////////////////////////// +// +// Recieve Logic +// + +always @(posedge clk) + rxd_s <= #1 rxd_i; + +always @(posedge clk) + rxd_r <= #1 rxd_s; + +assign start = (rxd_r == IDLE_BIT) & (rxd_s == START_BIT); + +always @(posedge clk) + if(!rst) rx_bit_cnt <= #1 4'ha; + else + if(!rx_go & start) rx_bit_cnt <= #1 4'h0; + else + if(rx_go & rx_sio_ce) rx_bit_cnt <= #1 rx_bit_cnt + 4'h1; + +always @(posedge clk) + rx_go <= #1 (rx_bit_cnt != 4'ha); + +always @(posedge clk) + rx_valid <= #1 (rx_bit_cnt == 4'h9); + +always @(posedge clk) + rx_valid_r <= #1 rx_valid; + +assign rx_we = !rx_valid_r & rx_valid & !rxf_full; + +always @(posedge clk) + if(rx_go & rx_sio_ce) rxr <= {rxd_s, rxr[9:1]}; + +always @(posedge clk) + rts_o <= #1 rxf_full; + +/////////////////////////////////////////////////////////////////// +// +// Reciever DPLL +// + +// Uses 4x baud clock to lock to incoming stream + +// Edge detector +always @(posedge clk) + if(sio_ce_x4) rxd_r1 <= #1 rxd_s; + +always @(posedge clk) + if(sio_ce_x4) rxd_r2 <= #1 rxd_r1; + +always @(posedge clk) + if(!rst) change <= #1 1'b0; + else + if(rxd_r != rxd_s) change <= #1 1'b1; + else + if(sio_ce_x4) change <= #1 1'b0; + +// DPLL FSM +always @(posedge clk or negedge rst) + if(!rst) dpll_state <= #1 2'h1; + else + if(sio_ce_x4) dpll_state <= #1 dpll_next_state; + +always @(dpll_state or change) + begin + rx_sio_ce_d = 1'b0; + case(dpll_state) + 2'h0: + if(change) dpll_next_state = 3'h0; + else dpll_next_state = 3'h1; + 2'h1:begin + rx_sio_ce_d = 1'b1; + if(change) dpll_next_state = 3'h3; + else dpll_next_state = 3'h2; + end + 2'h2: + if(change) dpll_next_state = 3'h0; + else dpll_next_state = 3'h3; + 2'h3: + if(change) dpll_next_state = 3'h0; + else dpll_next_state = 3'h0; + endcase + end + +// Compensate for sync registers at the input - allign sio +// clock enable to be in the middle between two bit changes ... +always @(posedge clk) + rx_sio_ce_r1 <= #1 rx_sio_ce_d; + +always @(posedge clk) + rx_sio_ce_r2 <= #1 rx_sio_ce_r1; + +always @(posedge clk) + rx_sio_ce <= #1 rx_sio_ce_r1 & !rx_sio_ce_r2; + +endmodule + + diff --git a/tests/iwls2005/sasc/timescale.v b/tests/iwls2005/sasc/timescale.v new file mode 100644 index 000000000..ff9e265a8 --- /dev/null +++ b/tests/iwls2005/sasc/timescale.v @@ -0,0 +1 @@ +`timescale 1ns / 10ps diff --git a/tests/iwls2005/simple_spi/fifo4.v b/tests/iwls2005/simple_spi/fifo4.v new file mode 100644 index 000000000..f041c7d2f --- /dev/null +++ b/tests/iwls2005/simple_spi/fifo4.v @@ -0,0 +1,134 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// FIFO 4 entries deep //// +//// //// +//// Authors: Rudolf Usselmann, Richard Herveille //// +//// rudi@asics.ws richard@asics.ws //// +//// //// +//// //// +//// Download from: http://www.opencores.org/projects/sasc //// +//// http://www.opencores.org/projects/simple_spi //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann, Richard Herveille //// +//// www.asics.ws //// +//// rudi@asics.ws, richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: fifo4.v,v 1.1.1.1 2002/12/22 16:07:14 rherveille Exp $ +// +// $Date: 2002/12/22 16:07:14 $ +// $Revision: 1.1.1.1 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: fifo4.v,v $ +// Revision 1.1.1.1 2002/12/22 16:07:14 rherveille +// Initial release +// +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + + +// 4 entry deep fast fifo +module fifo4(clk, rst, clr, din, we, dout, re, full, empty); + +parameter dw = 8; + +input clk, rst; +input clr; +input [dw:1] din; +input we; +output [dw:1] dout; +input re; +output full, empty; + + +//////////////////////////////////////////////////////////////////// +// +// Local Wires +// + +reg [dw:1] mem[0:3]; +reg [1:0] wp; +reg [1:0] rp; +wire [1:0] wp_p1; +wire [1:0] wp_p2; +wire [1:0] rp_p1; +wire full, empty; +reg gb; + +//////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +always @(posedge clk or negedge rst) + if(!rst) wp <= #1 2'h0; + else + if(clr) wp <= #1 2'h0; + else + if(we) wp <= #1 wp_p1; + +assign wp_p1 = wp + 2'h1; +assign wp_p2 = wp + 2'h2; + +always @(posedge clk or negedge rst) + if(!rst) rp <= #1 2'h0; + else + if(clr) rp <= #1 2'h0; + else + if(re) rp <= #1 rp_p1; + +assign rp_p1 = rp + 2'h1; + +// Fifo Output +assign dout = mem[ rp ]; + +// Fifo Input +always @(posedge clk) + if(we) mem[ wp ] <= #1 din; + +// Status +assign empty = (wp == rp) & !gb; +assign full = (wp == rp) & gb; + +// Guard Bit ... +always @(posedge clk) + if(!rst) gb <= #1 1'b0; + else + if(clr) gb <= #1 1'b0; + else + if((wp_p1 == rp) & we) gb <= #1 1'b1; + else + if(re) gb <= #1 1'b0; + +endmodule diff --git a/tests/iwls2005/simple_spi/simple_spi_top.v b/tests/iwls2005/simple_spi/simple_spi_top.v new file mode 100644 index 000000000..e952f4bef --- /dev/null +++ b/tests/iwls2005/simple_spi/simple_spi_top.v @@ -0,0 +1,329 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// OpenCores MC68HC11E based SPI interface //// +//// //// +//// Author: Richard Herveille //// +//// richard@asics.ws //// +//// www.asics.ws //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2002 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: simple_spi_top.v,v 1.5 2004/02/28 15:59:50 rherveille Exp $ +// +// $Date: 2004/02/28 15:59:50 $ +// $Revision: 1.5 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: simple_spi_top.v,v $ +// Revision 1.5 2004/02/28 15:59:50 rherveille +// Fixed SCK_O generation bug. +// This resulted in a major rewrite of the serial interface engine. +// +// Revision 1.4 2003/08/01 11:41:54 rherveille +// Fixed some timing bugs. +// +// Revision 1.3 2003/01/09 16:47:59 rherveille +// Updated clkcnt size and decoding due to new SPR bit assignments. +// +// Revision 1.2 2003/01/07 13:29:52 rherveille +// Changed SPR bits coding. +// +// Revision 1.1.1.1 2002/12/22 16:07:15 rherveille +// Initial release +// +// + + + +// +// Motorola MC68HC11E based SPI interface +// +// Currently only MASTER mode is supported +// + +// synopsys translate_off +`include "timescale.v" +// synopsys translate_on + +module simple_spi_top( + // 8bit WISHBONE bus slave interface + input wire clk_i, // clock + input wire rst_i, // reset (asynchronous active low) + input wire cyc_i, // cycle + input wire stb_i, // strobe + input wire [1:0] adr_i, // address + input wire we_i, // write enable + input wire [7:0] dat_i, // data input + output reg [7:0] dat_o, // data output + output reg ack_o, // normal bus termination + output reg inta_o, // interrupt output + + // SPI port + output reg sck_o, // serial clock output + output wire mosi_o, // MasterOut SlaveIN + input wire miso_i // MasterIn SlaveOut +); + + // + // Module body + // + reg [7:0] spcr; // Serial Peripheral Control Register ('HC11 naming) + wire [7:0] spsr; // Serial Peripheral Status register ('HC11 naming) + reg [7:0] sper; // Serial Peripheral Extension register + reg [7:0] treg, rreg; // Transmit/Receive register + + // fifo signals + wire [7:0] rfdout; + reg wfre, rfwe; + wire rfre, rffull, rfempty; + wire [7:0] wfdout; + wire wfwe, wffull, wfempty; + + // misc signals + wire tirq; // transfer interrupt (selected number of transfers done) + wire wfov; // write fifo overrun (writing while fifo full) + reg [1:0] state; // statemachine state + reg [2:0] bcnt; + + // + // Wishbone interface + wire wb_acc = cyc_i & stb_i; // WISHBONE access + wire wb_wr = wb_acc & we_i; // WISHBONE write access + + // dat_i + always @(posedge clk_i or negedge rst_i) + if (~rst_i) + begin + spcr <= #1 8'h10; // set master bit + sper <= #1 8'h00; + end + else if (wb_wr) + begin + if (adr_i == 2'b00) + spcr <= #1 dat_i | 8'h10; // always set master bit + + if (adr_i == 2'b11) + sper <= #1 dat_i; + end + + // write fifo + assign wfwe = wb_acc & (adr_i == 2'b10) & ack_o & we_i; + assign wfov = wfwe & wffull; + + // dat_o + always @(posedge clk_i) + case(adr_i) // synopsys full_case parallel_case + 2'b00: dat_o <= #1 spcr; + 2'b01: dat_o <= #1 spsr; + 2'b10: dat_o <= #1 rfdout; + 2'b11: dat_o <= #1 sper; + endcase + + // read fifo + assign rfre = wb_acc & (adr_i == 2'b10) & ack_o & ~we_i; + + // ack_o + always @(posedge clk_i or negedge rst_i) + if (~rst_i) + ack_o <= #1 1'b0; + else + ack_o <= #1 wb_acc & !ack_o; + + // decode Serial Peripheral Control Register + wire spie = spcr[7]; // Interrupt enable bit + wire spe = spcr[6]; // System Enable bit + wire dwom = spcr[5]; // Port D Wired-OR Mode Bit + wire mstr = spcr[4]; // Master Mode Select Bit + wire cpol = spcr[3]; // Clock Polarity Bit + wire cpha = spcr[2]; // Clock Phase Bit + wire [1:0] spr = spcr[1:0]; // Clock Rate Select Bits + + // decode Serial Peripheral Extension Register + wire [1:0] icnt = sper[7:6]; // interrupt on transfer count + wire [1:0] spre = sper[1:0]; // extended clock rate select + + wire [3:0] espr = {spre, spr}; + + // generate status register + wire wr_spsr = wb_wr & (adr_i == 2'b01); + + reg spif; + always @(posedge clk_i) + if (~spe) + spif <= #1 1'b0; + else + spif <= #1 (tirq | spif) & ~(wr_spsr & dat_i[7]); + + reg wcol; + always @(posedge clk_i) + if (~spe) + wcol <= #1 1'b0; + else + wcol <= #1 (wfov | wcol) & ~(wr_spsr & dat_i[6]); + + assign spsr[7] = spif; + assign spsr[6] = wcol; + assign spsr[5:4] = 2'b00; + assign spsr[3] = wffull; + assign spsr[2] = wfempty; + assign spsr[1] = rffull; + assign spsr[0] = rfempty; + + + // generate IRQ output (inta_o) + always @(posedge clk_i) + inta_o <= #1 spif & spie; + + // + // hookup read/write buffer fifo + fifo4 #(8) + rfifo( + .clk ( clk_i ), + .rst ( rst_i ), + .clr ( ~spe ), + .din ( treg ), + .we ( rfwe ), + .dout ( rfdout ), + .re ( rfre ), + .full ( rffull ), + .empty ( rfempty ) + ), + wfifo( + .clk ( clk_i ), + .rst ( rst_i ), + .clr ( ~spe ), + .din ( dat_i ), + .we ( wfwe ), + .dout ( wfdout ), + .re ( wfre ), + .full ( wffull ), + .empty ( wfempty ) + ); + + // + // generate clk divider + reg [11:0] clkcnt; + always @(posedge clk_i) + if(spe & (|clkcnt & |state)) + clkcnt <= #1 clkcnt - 11'h1; + else + case (espr) // synopsys full_case parallel_case + 4'b0000: clkcnt <= #1 12'h0; // 2 -- original M68HC11 coding + 4'b0001: clkcnt <= #1 12'h1; // 4 -- original M68HC11 coding + 4'b0010: clkcnt <= #1 12'h3; // 16 -- original M68HC11 coding + 4'b0011: clkcnt <= #1 12'hf; // 32 -- original M68HC11 coding + 4'b0100: clkcnt <= #1 12'h1f; // 8 + 4'b0101: clkcnt <= #1 12'h7; // 64 + 4'b0110: clkcnt <= #1 12'h3f; // 128 + 4'b0111: clkcnt <= #1 12'h7f; // 256 + 4'b1000: clkcnt <= #1 12'hff; // 512 + 4'b1001: clkcnt <= #1 12'h1ff; // 1024 + 4'b1010: clkcnt <= #1 12'h3ff; // 2048 + 4'b1011: clkcnt <= #1 12'h7ff; // 4096 + endcase + + // generate clock enable signal + wire ena = ~|clkcnt; + + // transfer statemachine + always @(posedge clk_i) + if (~spe) + begin + state <= #1 2'b00; // idle + bcnt <= #1 3'h0; + treg <= #1 8'h00; + wfre <= #1 1'b0; + rfwe <= #1 1'b0; + sck_o <= #1 1'b0; + end + else + begin + wfre <= #1 1'b0; + rfwe <= #1 1'b0; + + case (state) //synopsys full_case parallel_case + 2'b00: // idle state + begin + bcnt <= #1 3'h7; // set transfer counter + treg <= #1 wfdout; // load transfer register + sck_o <= #1 cpol; // set sck + + if (~wfempty) begin + wfre <= #1 1'b1; + state <= #1 2'b01; + if (cpha) sck_o <= #1 ~sck_o; + end + end + + 2'b01: // clock-phase2, next data + if (ena) begin + sck_o <= #1 ~sck_o; + state <= #1 2'b11; + end + + 2'b11: // clock phase1 + if (ena) begin + treg <= #1 {treg[6:0], miso_i}; + bcnt <= #1 bcnt -3'h1; + + if (~|bcnt) begin + state <= #1 2'b00; + sck_o <= #1 cpol; + rfwe <= #1 1'b1; + end else begin + state <= #1 2'b01; + sck_o <= #1 ~sck_o; + end + end + + 2'b10: state <= #1 2'b00; + endcase + end + + assign mosi_o = treg[7]; + + + // count number of transfers (for interrupt generation) + reg [1:0] tcnt; // transfer count + always @(posedge clk_i) + if (~spe) + tcnt <= #1 icnt; + else if (rfwe) // rfwe gets asserted when all bits have been transfered + if (|tcnt) + tcnt <= #1 tcnt - 2'h1; + else + tcnt <= #1 icnt; + + assign tirq = ~|tcnt & rfwe; + +endmodule + diff --git a/tests/iwls2005/spi/spi_clgen.v b/tests/iwls2005/spi/spi_clgen.v new file mode 100644 index 000000000..7bc4f6e5e --- /dev/null +++ b/tests/iwls2005/spi/spi_clgen.v @@ -0,0 +1,108 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// spi_clgen.v //// +//// //// +//// This file is part of the SPI IP core project //// +//// http://www.opencores.org/projects/spi/ //// +//// //// +//// Author(s): //// +//// - Simon Srot (simons@opencores.org) //// +//// //// +//// All additional information is avaliable in the Readme.txt //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2002 Authors //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + +`include "spi_defines.v" +`include "timescale.v" + +module spi_clgen (clk_in, rst, go, enable, last_clk, divider, clk_out, pos_edge, neg_edge); + + parameter Tp = 1; + + input clk_in; // input clock (system clock) + input rst; // reset + input enable; // clock enable + input go; // start transfer + input last_clk; // last clock + input [`SPI_DIVIDER_LEN-1:0] divider; // clock divider (output clock is divided by this value) + output clk_out; // output clock + output pos_edge; // pulse marking positive edge of clk_out + output neg_edge; // pulse marking negative edge of clk_out + + reg clk_out; + reg pos_edge; + reg neg_edge; + + reg [`SPI_DIVIDER_LEN-1:0] cnt; // clock counter + wire cnt_zero; // conter is equal to zero + wire cnt_one; // conter is equal to one + + + assign cnt_zero = cnt == {`SPI_DIVIDER_LEN{1'b0}}; + assign cnt_one = cnt == {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; + + // Counter counts half period + always @(posedge clk_in or posedge rst) + begin + if(rst) + cnt <= #Tp {`SPI_DIVIDER_LEN{1'b1}}; + else + begin + if(!enable || cnt_zero) + cnt <= #Tp divider; + else + cnt <= #Tp cnt - {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; + end + end + + // clk_out is asserted every other half period + always @(posedge clk_in or posedge rst) + begin + if(rst) + clk_out <= #Tp 1'b0; + else + clk_out <= #Tp (enable && cnt_zero && (!last_clk || clk_out)) ? ~clk_out : clk_out; + end + + // Pos and neg edge signals + always @(posedge clk_in or posedge rst) + begin + if(rst) + begin + pos_edge <= #Tp 1'b0; + neg_edge <= #Tp 1'b0; + end + else + begin + pos_edge <= #Tp (enable && !clk_out && cnt_one) || (!(|divider) && clk_out) || (!(|divider) && go && !enable); + neg_edge <= #Tp (enable && clk_out && cnt_one) || (!(|divider) && !clk_out && enable); + end + end +endmodule + diff --git a/tests/iwls2005/spi/spi_defines.v b/tests/iwls2005/spi/spi_defines.v new file mode 100644 index 000000000..a6925918e --- /dev/null +++ b/tests/iwls2005/spi/spi_defines.v @@ -0,0 +1,159 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// spi_define.v //// +//// //// +//// This file is part of the SPI IP core project //// +//// http://www.opencores.org/projects/spi/ //// +//// //// +//// Author(s): //// +//// - Simon Srot (simons@opencores.org) //// +//// //// +//// All additional information is avaliable in the Readme.txt //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2002 Authors //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + +// +// Number of bits used for devider register. If used in system with +// low frequency of system clock this can be reduced. +// Use SPI_DIVIDER_LEN for fine tuning theexact number. +// +//`define SPI_DIVIDER_LEN_8 +`define SPI_DIVIDER_LEN_16 +//`define SPI_DIVIDER_LEN_24 +//`define SPI_DIVIDER_LEN_32 + +`ifdef SPI_DIVIDER_LEN_8 + `define SPI_DIVIDER_LEN 8 // Can be set from 1 to 8 +`endif +`ifdef SPI_DIVIDER_LEN_16 + `define SPI_DIVIDER_LEN 16 // Can be set from 9 to 16 +`endif +`ifdef SPI_DIVIDER_LEN_24 + `define SPI_DIVIDER_LEN 24 // Can be set from 17 to 24 +`endif +`ifdef SPI_DIVIDER_LEN_32 + `define SPI_DIVIDER_LEN 32 // Can be set from 25 to 32 +`endif + +// +// Maximum nuber of bits that can be send/received at once. +// Use SPI_MAX_CHAR for fine tuning the exact number, when using +// SPI_MAX_CHAR_32, SPI_MAX_CHAR_24, SPI_MAX_CHAR_16, SPI_MAX_CHAR_8. +// +`define SPI_MAX_CHAR_128 +//`define SPI_MAX_CHAR_64 +//`define SPI_MAX_CHAR_32 +//`define SPI_MAX_CHAR_24 +//`define SPI_MAX_CHAR_16 +//`define SPI_MAX_CHAR_8 + +`ifdef SPI_MAX_CHAR_128 + `define SPI_MAX_CHAR 128 // Can only be set to 128 + `define SPI_CHAR_LEN_BITS 7 +`endif +`ifdef SPI_MAX_CHAR_64 + `define SPI_MAX_CHAR 64 // Can only be set to 64 + `define SPI_CHAR_LEN_BITS 6 +`endif +`ifdef SPI_MAX_CHAR_32 + `define SPI_MAX_CHAR 32 // Can be set from 25 to 32 + `define SPI_CHAR_LEN_BITS 5 +`endif +`ifdef SPI_MAX_CHAR_24 + `define SPI_MAX_CHAR 24 // Can be set from 17 to 24 + `define SPI_CHAR_LEN_BITS 5 +`endif +`ifdef SPI_MAX_CHAR_16 + `define SPI_MAX_CHAR 16 // Can be set from 9 to 16 + `define SPI_CHAR_LEN_BITS 4 +`endif +`ifdef SPI_MAX_CHAR_8 + `define SPI_MAX_CHAR 8 // Can be set from 1 to 8 + `define SPI_CHAR_LEN_BITS 3 +`endif + +// +// Number of device select signals. Use SPI_SS_NB for fine tuning the +// exact number. +// +`define SPI_SS_NB_8 +//`define SPI_SS_NB_16 +//`define SPI_SS_NB_24 +//`define SPI_SS_NB_32 + +`ifdef SPI_SS_NB_8 + `define SPI_SS_NB 8 // Can be set from 1 to 8 +`endif +`ifdef SPI_SS_NB_16 + `define SPI_SS_NB 16 // Can be set from 9 to 16 +`endif +`ifdef SPI_SS_NB_24 + `define SPI_SS_NB 24 // Can be set from 17 to 24 +`endif +`ifdef SPI_SS_NB_32 + `define SPI_SS_NB 32 // Can be set from 25 to 32 +`endif + +// +// Bits of WISHBONE address used for partial decoding of SPI registers. +// +`define SPI_OFS_BITS 4:2 + +// +// Register offset +// +`define SPI_RX_0 0 +`define SPI_RX_1 1 +`define SPI_RX_2 2 +`define SPI_RX_3 3 +`define SPI_TX_0 0 +`define SPI_TX_1 1 +`define SPI_TX_2 2 +`define SPI_TX_3 3 +`define SPI_CTRL 4 +`define SPI_DEVIDE 5 +`define SPI_SS 6 + +// +// Number of bits in ctrl register +// +`define SPI_CTRL_BIT_NB 14 + +// +// Control register bit position +// +`define SPI_CTRL_ASS 13 +`define SPI_CTRL_IE 12 +`define SPI_CTRL_LSB 11 +`define SPI_CTRL_TX_NEGEDGE 10 +`define SPI_CTRL_RX_NEGEDGE 9 +`define SPI_CTRL_GO 8 +`define SPI_CTRL_RES_1 7 +`define SPI_CTRL_CHAR_LEN 6:0 + diff --git a/tests/iwls2005/spi/spi_shift.v b/tests/iwls2005/spi/spi_shift.v new file mode 100644 index 000000000..b17ac8b1f --- /dev/null +++ b/tests/iwls2005/spi/spi_shift.v @@ -0,0 +1,238 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// spi_shift.v //// +//// //// +//// This file is part of the SPI IP core project //// +//// http://www.opencores.org/projects/spi/ //// +//// //// +//// Author(s): //// +//// - Simon Srot (simons@opencores.org) //// +//// //// +//// All additional information is avaliable in the Readme.txt //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2002 Authors //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + +`include "spi_defines.v" +`include "timescale.v" + +module spi_shift (clk, rst, latch, byte_sel, len, lsb, go, + pos_edge, neg_edge, rx_negedge, tx_negedge, + tip, last, + p_in, p_out, s_clk, s_in, s_out); + + parameter Tp = 1; + + input clk; // system clock + input rst; // reset + input [3:0] latch; // latch signal for storing the data in shift register + input [3:0] byte_sel; // byte select signals for storing the data in shift register + input [`SPI_CHAR_LEN_BITS-1:0] len; // data len in bits (minus one) + input lsb; // lbs first on the line + input go; // start stansfer + input pos_edge; // recognize posedge of sclk + input neg_edge; // recognize negedge of sclk + input rx_negedge; // s_in is sampled on negative edge + input tx_negedge; // s_out is driven on negative edge + output tip; // transfer in progress + output last; // last bit + input [31:0] p_in; // parallel in + output [`SPI_MAX_CHAR-1:0] p_out; // parallel out + input s_clk; // serial clock + input s_in; // serial in + output s_out; // serial out + + reg s_out; + reg tip; + + reg [`SPI_CHAR_LEN_BITS:0] cnt; // data bit count + reg [`SPI_MAX_CHAR-1:0] data; // shift register + wire [`SPI_CHAR_LEN_BITS:0] tx_bit_pos; // next bit position + wire [`SPI_CHAR_LEN_BITS:0] rx_bit_pos; // next bit position + wire rx_clk; // rx clock enable + wire tx_clk; // tx clock enable + + assign p_out = data; + + assign tx_bit_pos = lsb ? {!(|len), len} - cnt : cnt - {{`SPI_CHAR_LEN_BITS{1'b0}},1'b1}; + assign rx_bit_pos = lsb ? {!(|len), len} - (rx_negedge ? cnt + {{`SPI_CHAR_LEN_BITS{1'b0}},1'b1} : cnt) : + (rx_negedge ? cnt : cnt - {{`SPI_CHAR_LEN_BITS{1'b0}},1'b1}); + + assign last = !(|cnt); + + assign rx_clk = (rx_negedge ? neg_edge : pos_edge) && (!last || s_clk); + assign tx_clk = (tx_negedge ? neg_edge : pos_edge) && !last; + + // Character bit counter + always @(posedge clk or posedge rst) + begin + if(rst) + cnt <= #Tp {`SPI_CHAR_LEN_BITS+1{1'b0}}; + else + begin + if(tip) + cnt <= #Tp pos_edge ? (cnt - {{`SPI_CHAR_LEN_BITS{1'b0}}, 1'b1}) : cnt; + else + cnt <= #Tp !(|len) ? {1'b1, {`SPI_CHAR_LEN_BITS{1'b0}}} : {1'b0, len}; + end + end + + // Transfer in progress + always @(posedge clk or posedge rst) + begin + if(rst) + tip <= #Tp 1'b0; + else if(go && ~tip) + tip <= #Tp 1'b1; + else if(tip && last && pos_edge) + tip <= #Tp 1'b0; + end + + // Sending bits to the line + always @(posedge clk or posedge rst) + begin + if (rst) + s_out <= #Tp 1'b0; + else + s_out <= #Tp (tx_clk || !tip) ? data[tx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] : s_out; + end + + // Receiving bits from the line + always @(posedge clk or posedge rst) + begin + if (rst) + data <= #Tp {`SPI_MAX_CHAR{1'b0}}; +`ifdef SPI_MAX_CHAR_128 + else if (latch[0] && !tip) + begin + if (byte_sel[3]) + data[31:24] <= #Tp p_in[31:24]; + if (byte_sel[2]) + data[23:16] <= #Tp p_in[23:16]; + if (byte_sel[1]) + data[15:8] <= #Tp p_in[15:8]; + if (byte_sel[0]) + data[7:0] <= #Tp p_in[7:0]; + end + else if (latch[1] && !tip) + begin + if (byte_sel[3]) + data[63:56] <= #Tp p_in[31:24]; + if (byte_sel[2]) + data[55:48] <= #Tp p_in[23:16]; + if (byte_sel[1]) + data[47:40] <= #Tp p_in[15:8]; + if (byte_sel[0]) + data[39:32] <= #Tp p_in[7:0]; + end + else if (latch[2] && !tip) + begin + if (byte_sel[3]) + data[95:88] <= #Tp p_in[31:24]; + if (byte_sel[2]) + data[87:80] <= #Tp p_in[23:16]; + if (byte_sel[1]) + data[79:72] <= #Tp p_in[15:8]; + if (byte_sel[0]) + data[71:64] <= #Tp p_in[7:0]; + end + else if (latch[3] && !tip) + begin + if (byte_sel[3]) + data[127:120] <= #Tp p_in[31:24]; + if (byte_sel[2]) + data[119:112] <= #Tp p_in[23:16]; + if (byte_sel[1]) + data[111:104] <= #Tp p_in[15:8]; + if (byte_sel[0]) + data[103:96] <= #Tp p_in[7:0]; + end +`else +`ifdef SPI_MAX_CHAR_64 + else if (latch[0] && !tip) + begin + if (byte_sel[3]) + data[31:24] <= #Tp p_in[31:24]; + if (byte_sel[2]) + data[23:16] <= #Tp p_in[23:16]; + if (byte_sel[1]) + data[15:8] <= #Tp p_in[15:8]; + if (byte_sel[0]) + data[7:0] <= #Tp p_in[7:0]; + end + else if (latch[1] && !tip) + begin + if (byte_sel[3]) + data[63:56] <= #Tp p_in[31:24]; + if (byte_sel[2]) + data[55:48] <= #Tp p_in[23:16]; + if (byte_sel[1]) + data[47:40] <= #Tp p_in[15:8]; + if (byte_sel[0]) + data[39:32] <= #Tp p_in[7:0]; + end +`else + else if (latch[0] && !tip) + begin + `ifdef SPI_MAX_CHAR_8 + if (byte_sel[0]) + data[`SPI_MAX_CHAR-1:0] <= #Tp p_in[`SPI_MAX_CHAR-1:0]; + `endif + `ifdef SPI_MAX_CHAR_16 + if (byte_sel[0]) + data[7:0] <= #Tp p_in[7:0]; + if (byte_sel[1]) + data[`SPI_MAX_CHAR-1:8] <= #Tp p_in[`SPI_MAX_CHAR-1:8]; + `endif + `ifdef SPI_MAX_CHAR_24 + if (byte_sel[0]) + data[7:0] <= #Tp p_in[7:0]; + if (byte_sel[1]) + data[15:8] <= #Tp p_in[15:8]; + if (byte_sel[2]) + data[`SPI_MAX_CHAR-1:16] <= #Tp p_in[`SPI_MAX_CHAR-1:16]; + `endif + `ifdef SPI_MAX_CHAR_32 + if (byte_sel[0]) + data[7:0] <= #Tp p_in[7:0]; + if (byte_sel[1]) + data[15:8] <= #Tp p_in[15:8]; + if (byte_sel[2]) + data[23:16] <= #Tp p_in[23:16]; + if (byte_sel[3]) + data[`SPI_MAX_CHAR-1:24] <= #Tp p_in[`SPI_MAX_CHAR-1:24]; + `endif + end +`endif +`endif + else + data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] <= #Tp rx_clk ? s_in : data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]]; + end + +endmodule + diff --git a/tests/iwls2005/spi/spi_top.v b/tests/iwls2005/spi/spi_top.v new file mode 100644 index 000000000..09b2e50e1 --- /dev/null +++ b/tests/iwls2005/spi/spi_top.v @@ -0,0 +1,287 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// spi_top.v //// +//// //// +//// This file is part of the SPI IP core project //// +//// http://www.opencores.org/projects/spi/ //// +//// //// +//// Author(s): //// +//// - Simon Srot (simons@opencores.org) //// +//// //// +//// All additional information is avaliable in the Readme.txt //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2002 Authors //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + + +`include "spi_defines.v" +`include "timescale.v" + +module spi_top +( + // Wishbone signals + wb_clk_i, wb_rst_i, wb_adr_i, wb_dat_i, wb_dat_o, wb_sel_i, + wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_err_o, wb_int_o, + + // SPI signals + ss_pad_o, sclk_pad_o, mosi_pad_o, miso_pad_i +); + + parameter Tp = 1; + + // Wishbone signals + input wb_clk_i; // master clock input + input wb_rst_i; // synchronous active high reset + input [4:0] wb_adr_i; // lower address bits + input [32-1:0] wb_dat_i; // databus input + output [32-1:0] wb_dat_o; // databus output + input [3:0] wb_sel_i; // byte select inputs + input wb_we_i; // write enable input + input wb_stb_i; // stobe/core select signal + input wb_cyc_i; // valid bus cycle input + output wb_ack_o; // bus cycle acknowledge output + output wb_err_o; // termination w/ error + output wb_int_o; // interrupt request signal output + + // SPI signals + output [`SPI_SS_NB-1:0] ss_pad_o; // slave select + output sclk_pad_o; // serial clock + output mosi_pad_o; // master out slave in + input miso_pad_i; // master in slave out + + reg [32-1:0] wb_dat_o; + reg wb_ack_o; + reg wb_int_o; + + // Internal signals + reg [`SPI_DIVIDER_LEN-1:0] divider; // Divider register + reg [`SPI_CTRL_BIT_NB-1:0] ctrl; // Control and status register + reg [`SPI_SS_NB-1:0] ss; // Slave select register + reg [32-1:0] wb_dat; // wb data out + wire [`SPI_MAX_CHAR-1:0] rx; // Rx register + wire rx_negedge; // miso is sampled on negative edge + wire tx_negedge; // mosi is driven on negative edge + wire [`SPI_CHAR_LEN_BITS-1:0] char_len; // char len + wire go; // go + wire lsb; // lsb first on line + wire ie; // interrupt enable + wire ass; // automatic slave select + wire spi_divider_sel; // divider register select + wire spi_ctrl_sel; // ctrl register select + wire [3:0] spi_tx_sel; // tx_l register select + wire spi_ss_sel; // ss register select + wire tip; // transfer in progress + wire pos_edge; // recognize posedge of sclk + wire neg_edge; // recognize negedge of sclk + wire last_bit; // marks last character bit + + // Address decoder + assign spi_divider_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_DEVIDE); + assign spi_ctrl_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_CTRL); + assign spi_tx_sel[0] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_0); + assign spi_tx_sel[1] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_1); + assign spi_tx_sel[2] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_2); + assign spi_tx_sel[3] = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_3); + assign spi_ss_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_SS); + + // Read from registers + always @(wb_adr_i or rx or ctrl or divider or ss) + begin + case (wb_adr_i[`SPI_OFS_BITS]) +`ifdef SPI_MAX_CHAR_128 + `SPI_RX_0: wb_dat = rx[31:0]; + `SPI_RX_1: wb_dat = rx[63:32]; + `SPI_RX_2: wb_dat = rx[95:64]; + `SPI_RX_3: wb_dat = {{128-`SPI_MAX_CHAR{1'b0}}, rx[`SPI_MAX_CHAR-1:96]}; +`else +`ifdef SPI_MAX_CHAR_64 + `SPI_RX_0: wb_dat = rx[31:0]; + `SPI_RX_1: wb_dat = {{64-`SPI_MAX_CHAR{1'b0}}, rx[`SPI_MAX_CHAR-1:32]}; + `SPI_RX_2: wb_dat = 32'b0; + `SPI_RX_3: wb_dat = 32'b0; +`else + `SPI_RX_0: wb_dat = {{32-`SPI_MAX_CHAR{1'b0}}, rx[`SPI_MAX_CHAR-1:0]}; + `SPI_RX_1: wb_dat = 32'b0; + `SPI_RX_2: wb_dat = 32'b0; + `SPI_RX_3: wb_dat = 32'b0; +`endif +`endif + `SPI_CTRL: wb_dat = {{32-`SPI_CTRL_BIT_NB{1'b0}}, ctrl}; + `SPI_DEVIDE: wb_dat = {{32-`SPI_DIVIDER_LEN{1'b0}}, divider}; + `SPI_SS: wb_dat = {{32-`SPI_SS_NB{1'b0}}, ss}; + default: wb_dat = 32'bx; + endcase + end + + // Wb data out + always @(posedge wb_clk_i or posedge wb_rst_i) + begin + if (wb_rst_i) + wb_dat_o <= #Tp 32'b0; + else + wb_dat_o <= #Tp wb_dat; + end + + // Wb acknowledge + always @(posedge wb_clk_i or posedge wb_rst_i) + begin + if (wb_rst_i) + wb_ack_o <= #Tp 1'b0; + else + wb_ack_o <= #Tp wb_cyc_i & wb_stb_i & ~wb_ack_o; + end + + // Wb error + assign wb_err_o = 1'b0; + + // Interrupt + always @(posedge wb_clk_i or posedge wb_rst_i) + begin + if (wb_rst_i) + wb_int_o <= #Tp 1'b0; + else if (ie && tip && last_bit && pos_edge) + wb_int_o <= #Tp 1'b1; + else if (wb_ack_o) + wb_int_o <= #Tp 1'b0; + end + + // Divider register + always @(posedge wb_clk_i or posedge wb_rst_i) + begin + if (wb_rst_i) + divider <= #Tp {`SPI_DIVIDER_LEN{1'b0}}; + else if (spi_divider_sel && wb_we_i && !tip) + begin + `ifdef SPI_DIVIDER_LEN_8 + if (wb_sel_i[0]) + divider <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:0]; + `endif + `ifdef SPI_DIVIDER_LEN_16 + if (wb_sel_i[0]) + divider[7:0] <= #Tp wb_dat_i[7:0]; + if (wb_sel_i[1]) + divider[`SPI_DIVIDER_LEN-1:8] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:8]; + `endif + `ifdef SPI_DIVIDER_LEN_24 + if (wb_sel_i[0]) + divider[7:0] <= #Tp wb_dat_i[7:0]; + if (wb_sel_i[1]) + divider[15:8] <= #Tp wb_dat_i[15:8]; + if (wb_sel_i[2]) + divider[`SPI_DIVIDER_LEN-1:16] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:16]; + `endif + `ifdef SPI_DIVIDER_LEN_32 + if (wb_sel_i[0]) + divider[7:0] <= #Tp wb_dat_i[7:0]; + if (wb_sel_i[1]) + divider[15:8] <= #Tp wb_dat_i[15:8]; + if (wb_sel_i[2]) + divider[23:16] <= #Tp wb_dat_i[23:16]; + if (wb_sel_i[3]) + divider[`SPI_DIVIDER_LEN-1:24] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:24]; + `endif + end + end + + // Ctrl register + always @(posedge wb_clk_i or posedge wb_rst_i) + begin + if (wb_rst_i) + ctrl <= #Tp {`SPI_CTRL_BIT_NB{1'b0}}; + else if(spi_ctrl_sel && wb_we_i && !tip) + begin + if (wb_sel_i[0]) + ctrl[7:0] <= #Tp wb_dat_i[7:0] | {7'b0, ctrl[0]}; + if (wb_sel_i[1]) + ctrl[`SPI_CTRL_BIT_NB-1:8] <= #Tp wb_dat_i[`SPI_CTRL_BIT_NB-1:8]; + end + else if(tip && last_bit && pos_edge) + ctrl[`SPI_CTRL_GO] <= #Tp 1'b0; + end + + assign rx_negedge = ctrl[`SPI_CTRL_RX_NEGEDGE]; + assign tx_negedge = ctrl[`SPI_CTRL_TX_NEGEDGE]; + assign go = ctrl[`SPI_CTRL_GO]; + assign char_len = ctrl[`SPI_CTRL_CHAR_LEN]; + assign lsb = ctrl[`SPI_CTRL_LSB]; + assign ie = ctrl[`SPI_CTRL_IE]; + assign ass = ctrl[`SPI_CTRL_ASS]; + + // Slave select register + always @(posedge wb_clk_i or posedge wb_rst_i) + begin + if (wb_rst_i) + ss <= #Tp {`SPI_SS_NB{1'b0}}; + else if(spi_ss_sel && wb_we_i && !tip) + begin + `ifdef SPI_SS_NB_8 + if (wb_sel_i[0]) + ss <= #Tp wb_dat_i[`SPI_SS_NB-1:0]; + `endif + `ifdef SPI_SS_NB_16 + if (wb_sel_i[0]) + ss[7:0] <= #Tp wb_dat_i[7:0]; + if (wb_sel_i[1]) + ss[`SPI_SS_NB-1:8] <= #Tp wb_dat_i[`SPI_SS_NB-1:8]; + `endif + `ifdef SPI_SS_NB_24 + if (wb_sel_i[0]) + ss[7:0] <= #Tp wb_dat_i[7:0]; + if (wb_sel_i[1]) + ss[15:8] <= #Tp wb_dat_i[15:8]; + if (wb_sel_i[2]) + ss[`SPI_SS_NB-1:16] <= #Tp wb_dat_i[`SPI_SS_NB-1:16]; + `endif + `ifdef SPI_SS_NB_32 + if (wb_sel_i[0]) + ss[7:0] <= #Tp wb_dat_i[7:0]; + if (wb_sel_i[1]) + ss[15:8] <= #Tp wb_dat_i[15:8]; + if (wb_sel_i[2]) + ss[23:16] <= #Tp wb_dat_i[23:16]; + if (wb_sel_i[3]) + ss[`SPI_SS_NB-1:24] <= #Tp wb_dat_i[`SPI_SS_NB-1:24]; + `endif + end + end + + assign ss_pad_o = ~((ss & {`SPI_SS_NB{tip & ass}}) | (ss & {`SPI_SS_NB{!ass}})); + + spi_clgen clgen (.clk_in(wb_clk_i), .rst(wb_rst_i), .go(go), .enable(tip), .last_clk(last_bit), + .divider(divider), .clk_out(sclk_pad_o), .pos_edge(pos_edge), + .neg_edge(neg_edge)); + + spi_shift shift (.clk(wb_clk_i), .rst(wb_rst_i), .len(char_len[`SPI_CHAR_LEN_BITS-1:0]), + .latch(spi_tx_sel[3:0] & {4{wb_we_i}}), .byte_sel(wb_sel_i), .lsb(lsb), + .go(go), .pos_edge(pos_edge), .neg_edge(neg_edge), + .rx_negedge(rx_negedge), .tx_negedge(tx_negedge), + .tip(tip), .last(last_bit), + .p_in(wb_dat_i), .p_out(rx), + .s_clk(sclk_pad_o), .s_in(miso_pad_i), .s_out(mosi_pad_o)); +endmodule + diff --git a/tests/iwls2005/spi/timescale.v b/tests/iwls2005/spi/timescale.v new file mode 100644 index 000000000..60d4ecbd1 --- /dev/null +++ b/tests/iwls2005/spi/timescale.v @@ -0,0 +1,2 @@ +`timescale 1ns / 10ps + diff --git a/tests/iwls2005/ss_pcm/pcm_slv_top.v b/tests/iwls2005/ss_pcm/pcm_slv_top.v new file mode 100644 index 000000000..2548d35fa --- /dev/null +++ b/tests/iwls2005/ss_pcm/pcm_slv_top.v @@ -0,0 +1,222 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// PCM IO Slave Module //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/ss_pcm/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: pcm_slv_top.v,v 1.2 2002/09/17 15:32:50 rudi Exp $ +// +// $Date: 2002/09/17 15:32:50 $ +// $Revision: 1.2 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: pcm_slv_top.v,v $ +// Revision 1.2 2002/09/17 15:32:50 rudi +// *** empty log message *** +// +// Revision 1.1.1.1 2002/09/17 15:17:25 rudi +// Initial Checkin +// +// +// +// +// +// +// +// + +`include "timescale.v" + +/* +PCM Interface +=============================== +PCM_CLK +PCM_SYNC +PCM_DIN +PCM_DOUT +*/ + +module pcm_slv_top( clk, rst, + + ssel, + + // PCM + pcm_clk_i, pcm_sync_i, pcm_din_i, pcm_dout_o, + + // Internal Interface + din_i, dout_o, re_i, we_i); + +input clk, rst; +input [2:0] ssel; // Number of bits to delay (0-7) +input pcm_clk_i, pcm_sync_i, pcm_din_i; +output pcm_dout_o; +input [7:0] din_i; +output [7:0] dout_o; +input re_i; +input [1:0] we_i; + +/////////////////////////////////////////////////////////////////// +// +// Local Wires and Registers +// + +reg pclk_t, pclk_s, pclk_r; +wire pclk_ris, pclk_fal; +reg psync; +reg pcm_sync_r1, pcm_sync_r2, pcm_sync_r3; +reg tx_go; +wire tx_data_le; +reg [15:0] tx_hold_reg; +reg [7:0] tx_hold_byte_h, tx_hold_byte_l; +reg [3:0] tx_cnt; +wire tx_done; +reg [15:0] rx_hold_reg, rx_reg; +wire rx_data_le; +reg rxd_t, rxd; +reg tx_go_r1, tx_go_r2; +reg [7:0] psa; + +/////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +always @(posedge clk) + pclk_t <= #1 pcm_clk_i; + +always @(posedge clk) + pclk_s <= #1 pclk_t; + +always @(posedge clk) + pclk_r <= #1 pclk_s; + +assign pclk_ris = !pclk_r & pclk_s; +assign pclk_fal = pclk_r & !pclk_s; + +/////////////////////////////////////////////////////////////////// +// +// Retrieve Sync Signal +// + +always @(posedge clk) // Latch it at falling edge + if(pclk_fal) pcm_sync_r1 <= #1 pcm_sync_i; + +always @(posedge clk) // resync to rising edge + if(pclk_ris) psa <= #1 {psa[6:0], pcm_sync_r1}; + +always @(posedge clk) //delay bit N + pcm_sync_r2 <= #1 psa[ssel]; + +always @(posedge clk) // edge detector + pcm_sync_r3 <= #1 pcm_sync_r2; + +always @(posedge clk) + psync <= #1 !pcm_sync_r3 & pcm_sync_r2; + +/////////////////////////////////////////////////////////////////// +// +// Transmit Logic +// + +assign tx_data_le = tx_go & pclk_ris; + +always @(posedge clk) + if(we_i[1]) tx_hold_byte_h <= #1 din_i; + +always @(posedge clk) + if(we_i[0]) tx_hold_byte_l <= #1 din_i; + +always @(posedge clk) + if(!rst) tx_go <= #1 1'b0; + else + if(psync) tx_go <= #1 1'b1; + else + if(tx_done) tx_go <= #1 1'b0; + +always @(posedge clk) + if(!rst) tx_hold_reg <= #1 16'h0; + else + if(psync) tx_hold_reg <= #1 {tx_hold_byte_h, tx_hold_byte_l}; + else + if(tx_data_le) tx_hold_reg <= #1 {tx_hold_reg[14:0],1'b0}; + +assign pcm_dout_o = tx_hold_reg[15]; + +always @(posedge clk) + if(!rst) tx_cnt <= #1 4'h0; + else + if(tx_data_le) tx_cnt <= tx_cnt + 4'h1; + +assign tx_done = (tx_cnt == 4'hf) & tx_data_le; + +/////////////////////////////////////////////////////////////////// +// +// Recieve Logic +// + +always @(posedge clk) + if(pclk_ris) tx_go_r1 <= #1 tx_go; + +always @(posedge clk) + if(pclk_ris) tx_go_r2 <= #1 tx_go_r1; + +// Receive is in sync with transmit ... +always @(posedge clk) + if(pclk_fal) rxd_t <= #1 pcm_din_i; + +always @(posedge clk) + rxd <= #1 rxd_t; + +assign rx_data_le = (tx_go_r1 | tx_go) & pclk_fal; + +always @(posedge clk) + if(!rst) rx_hold_reg <= #1 16'h0; + else + if(rx_data_le) rx_hold_reg <= #1 {rx_hold_reg[14:0], rxd}; + +always @(posedge clk) + if(!rst) rx_reg <= #1 16'h0; + else + if(tx_go_r1 & !tx_go & pclk_ris) rx_reg <= #1 rx_hold_reg; + +assign dout_o = re_i ? rx_reg[15:8] : rx_reg[7:0]; + +endmodule + diff --git a/tests/iwls2005/ss_pcm/timescale.v b/tests/iwls2005/ss_pcm/timescale.v new file mode 100644 index 000000000..ff9e265a8 --- /dev/null +++ b/tests/iwls2005/ss_pcm/timescale.v @@ -0,0 +1 @@ +`timescale 1ns / 10ps diff --git a/tests/iwls2005/systemcaes/aes.v b/tests/iwls2005/systemcaes/aes.v new file mode 100644 index 000000000..e5021ed16 --- /dev/null +++ b/tests/iwls2005/systemcaes/aes.v @@ -0,0 +1,358 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// AES top file //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// AES top //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: aes.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + +module aes(clk,reset,load_i,decrypt_i,data_i,key_i,ready_o,data_o); +input clk; +input reset; +input load_i; +input decrypt_i; +input [127:0] data_i; +input [127:0] key_i; +output ready_o; +output [127:0] data_o; + +reg ready_o; +reg [127:0] data_o; + +reg next_ready_o; +reg keysched_start_i; +reg [3:0] keysched_round_i; +reg [127:0] keysched_last_key_i; +wire [127:0] keysched_new_key_o; + +wire keysched_ready_o; + +wire keysched_sbox_access_o; + +wire [7:0] keysched_sbox_data_o; + +wire keysched_sbox_decrypt_o; + +reg mixcol_start_i; +reg [127:0] mixcol_data_i; +wire mixcol_ready_o; + +wire [127:0] mixcol_data_o; + +reg subbytes_start_i; +reg [127:0] subbytes_data_i; +wire subbytes_ready_o; + +wire [127:0] subbytes_data_o; + +wire [7:0] subbytes_sbox_data_o; + +wire subbytes_sbox_decrypt_o; + +wire [7:0] sbox_data_o; + +reg [7:0] sbox_data_i; +reg sbox_decrypt_i; +reg state; +reg next_state; +reg [3:0] round; +reg [3:0] next_round; +reg [127:0] addroundkey_data_o; +reg [127:0] next_addroundkey_data_reg; +reg [127:0] addroundkey_data_reg; +reg [127:0] addroundkey_data_i; +reg addroundkey_ready_o; +reg next_addroundkey_ready_o; +reg addroundkey_start_i; +reg next_addroundkey_start_i; +reg [3:0] addroundkey_round; +reg [3:0] next_addroundkey_round; +reg first_round_reg; +reg next_first_round_reg; + +sbox sbox1 (.clk(clk), .reset(reset), .data_i(sbox_data_i), .decrypt_i(sbox_decrypt_i), .data_o(sbox_data_o)); +subbytes sub1 (.clk(clk), .reset(reset), .start_i(subbytes_start_i), .decrypt_i(decrypt_i), .data_i(subbytes_data_i), .ready_o(subbytes_ready_o), .data_o(subbytes_data_o), .sbox_data_o(subbytes_sbox_data_o), .sbox_data_i(sbox_data_o), .sbox_decrypt_o(subbytes_sbox_decrypt_o)); +mixcolum mix1 (.clk(clk), .reset(reset), .decrypt_i(decrypt_i), .start_i(mixcol_start_i), .data_i(mixcol_data_i), .ready_o(mixcol_ready_o), .data_o(mixcol_data_o)); +keysched ks1 (.clk(clk), .reset(reset), .start_i(keysched_start_i), .round_i(keysched_round_i), .last_key_i(keysched_last_key_i), .new_key_o(keysched_new_key_o), .ready_o(keysched_ready_o), .sbox_access_o(keysched_sbox_access_o), .sbox_data_o(keysched_sbox_data_o), .sbox_data_i(sbox_data_o), .sbox_decrypt_o(keysched_sbox_decrypt_o)); + +//registers: +always @(posedge clk or negedge reset) + +begin + + if(!reset) +begin + + state = (0); + ready_o = (0); + round = (0); + addroundkey_round = (0); + addroundkey_data_reg = (0); + addroundkey_ready_o = (0); + addroundkey_start_i = (0); + first_round_reg = (0); + +end +else +begin + + state = (next_state); + ready_o = (next_ready_o); + round = (next_round); + addroundkey_round = (next_addroundkey_round); + addroundkey_data_reg = (next_addroundkey_data_reg); + addroundkey_ready_o = (next_addroundkey_ready_o); + first_round_reg = (next_first_round_reg); + addroundkey_start_i = (next_addroundkey_start_i); + +end + + +end +//control: +always @( state or round or addroundkey_data_o or data_i or load_i or decrypt_i or addroundkey_ready_o or mixcol_ready_o or subbytes_ready_o or subbytes_data_o or mixcol_data_o or first_round_reg) + +begin + + + next_state = (state); + next_round = (round); + data_o = (addroundkey_data_o); + next_ready_o = (0); + + //Tokeyschedulemodule + + next_first_round_reg = (0); + + + subbytes_data_i = (0); + mixcol_data_i = (0); + addroundkey_data_i = (0); + next_addroundkey_start_i = (first_round_reg); + mixcol_start_i = ((addroundkey_ready_o&decrypt_i&round!=10)|(subbytes_ready_o&!decrypt_i)); + subbytes_start_i = ((addroundkey_ready_o&!decrypt_i)|(mixcol_ready_o&decrypt_i)|(addroundkey_ready_o&decrypt_i&round==10)); + + if(decrypt_i&&round!=10) + begin + addroundkey_data_i = (subbytes_data_o); + subbytes_data_i = (mixcol_data_o); + mixcol_data_i = (addroundkey_data_o); + end + else if(!decrypt_i&&round!=0) + begin + addroundkey_data_i = (mixcol_data_o); + subbytes_data_i = (addroundkey_data_o); + mixcol_data_i = (subbytes_data_o); + end + else + begin + mixcol_data_i = (subbytes_data_o); + subbytes_data_i = (addroundkey_data_o); + addroundkey_data_i = (data_i); + end + + + case(state) + + 0: + begin + if(load_i) + begin + next_state = (1); + if(decrypt_i) + next_round = (10); + else + next_round = (0); + next_first_round_reg = (1); + end + end + + 1: + begin + + //Counter + if(!decrypt_i&&mixcol_ready_o) + begin + next_addroundkey_start_i = (1); + addroundkey_data_i = (mixcol_data_o); + next_round = (round+1); + end + else if(decrypt_i&&subbytes_ready_o) + begin + next_addroundkey_start_i = (1); + addroundkey_data_i = (subbytes_data_o); + next_round = (round-1); + end + + //Output + if((round==9&&!decrypt_i)||(round==0&&decrypt_i)) + begin + next_addroundkey_start_i = (0); + mixcol_start_i = (0); + if(subbytes_ready_o) + begin + addroundkey_data_i = (subbytes_data_o); + next_addroundkey_start_i = (1); + next_round = (round+1); + end + end + + if((round==10&&!decrypt_i)||(round==0&&decrypt_i)) + begin + addroundkey_data_i = (subbytes_data_o); + subbytes_start_i = (0); + if(addroundkey_ready_o) + begin + next_ready_o = (1); + next_state = (0); + next_addroundkey_start_i = (0); + next_round = (0); + end + + end + + + end + + default: +begin + next_state = (0); + end + endcase + + +end +//addroundkey: +reg[127:0] data_var,round_data_var,round_key_var; +always @( addroundkey_data_i or addroundkey_start_i or addroundkey_data_reg or addroundkey_round or keysched_new_key_o or keysched_ready_o or key_i or round) + +begin + + + + round_data_var=addroundkey_data_reg; + next_addroundkey_data_reg = (addroundkey_data_reg); +next_addroundkey_ready_o = (0); + next_addroundkey_round = (addroundkey_round); + addroundkey_data_o = (addroundkey_data_reg); + + if(addroundkey_round==1||addroundkey_round==0) + keysched_last_key_i = (key_i); +else + keysched_last_key_i = (keysched_new_key_o); + + keysched_start_i = (0); + + keysched_round_i = (addroundkey_round); + + if(round==0&&addroundkey_start_i) +begin + + //Taketheinputandxorthemwithdataifround==0; + data_var=addroundkey_data_i; + round_key_var=key_i; + round_data_var=round_key_var^data_var; + next_addroundkey_data_reg = (round_data_var); +next_addroundkey_ready_o = (1); + +end +else if(addroundkey_start_i&&round!=0) +begin + + keysched_last_key_i = (key_i); + keysched_start_i = (1); + keysched_round_i = (1); + next_addroundkey_round = (1); + +end +else if(addroundkey_round!=round&&keysched_ready_o) +begin + +next_addroundkey_round = (addroundkey_round+1); + keysched_last_key_i = (keysched_new_key_o); + keysched_start_i = (1); + keysched_round_i = (addroundkey_round+1); + +end +else if(addroundkey_round==round&&keysched_ready_o) +begin + + data_var=addroundkey_data_i; + round_key_var=keysched_new_key_o; + round_data_var=round_key_var^data_var; + next_addroundkey_data_reg = (round_data_var); +next_addroundkey_ready_o = (1); + next_addroundkey_round = (0); + +end + + +end +//sbox_muxes: +always @( keysched_sbox_access_o or keysched_sbox_decrypt_o or keysched_sbox_data_o or subbytes_sbox_decrypt_o or subbytes_sbox_data_o) + +begin + + + if(keysched_sbox_access_o) +begin + + sbox_decrypt_i = (keysched_sbox_decrypt_o); + sbox_data_i = (keysched_sbox_data_o); + +end +else +begin + + sbox_decrypt_i = (subbytes_sbox_decrypt_o); +sbox_data_i = (subbytes_sbox_data_o); + +end + + +end + +endmodule diff --git a/tests/iwls2005/systemcaes/byte_mixcolum.v b/tests/iwls2005/systemcaes/byte_mixcolum.v new file mode 100644 index 000000000..b248cc400 --- /dev/null +++ b/tests/iwls2005/systemcaes/byte_mixcolum.v @@ -0,0 +1,92 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// Mixcolumns for 8 bit //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// Mixcolum for a byte //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: byte_mixcolum.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + +module byte_mixcolum(a,b,c,d,outx,outy); + +input [7:0] a,b,c,d; +output [7:0] outx, outy; + +reg [7:0] outx, outy; + +function [7:0] xtime; +input [7:0] in; +reg [3:0] xtime_t; + +begin +xtime[7:5] = in[6:4]; +xtime_t[3] = in[7]; +xtime_t[2] = in[7]; +xtime_t[1] = 0; +xtime_t[0] = in[7]; +xtime[4:1] =xtime_t^in[3:0]; +xtime[0] = in[7]; +end +endfunction + +reg [7:0] w1,w2,w3,w4,w5,w6,w7,w8,outx_var; +always @ (a, b, c, d) +begin +w1 = a ^b; +w2 = a ^c; +w3 = c ^d; +w4 = xtime(w1); +w5 = xtime(w3); +w6 = w2 ^w4 ^w5; +w7 = xtime(w6); +w8 = xtime(w7); + +outx_var = b^w3^w4; +outx=outx_var; +outy=w8^outx_var; + +end + +endmodule diff --git a/tests/iwls2005/systemcaes/keysched.v b/tests/iwls2005/systemcaes/keysched.v new file mode 100644 index 000000000..f242c5675 --- /dev/null +++ b/tests/iwls2005/systemcaes/keysched.v @@ -0,0 +1,248 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// Key schedule //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// Generate the next round key from the previous one //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: keysched.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + +module keysched(clk,reset,start_i,round_i,last_key_i,new_key_o,ready_o,sbox_access_o,sbox_data_o,sbox_data_i,sbox_decrypt_o); +input clk; +input reset; +input start_i; +input [3:0] round_i; +input [127:0] last_key_i; +output [127:0] new_key_o; +output ready_o; +output sbox_access_o; +output [7:0] sbox_data_o; +input [7:0] sbox_data_i; +output sbox_decrypt_o; + +reg [127:0] new_key_o; +reg ready_o; +reg sbox_access_o; +reg [7:0] sbox_data_o; +reg sbox_decrypt_o; + +reg [2:0] next_state; +reg [2:0] state; +reg [7:0] rcon_o; +reg [31:0] next_col; +reg [31:0] col; +reg [127:0] key_reg; +reg [127:0] next_key_reg; +reg next_ready_o; + + +//rcon: +always @( round_i) + +begin + + + case(round_i) + 1: +begin +rcon_o = (1); +end + 2: +begin +rcon_o = (2); +end + 3: +begin +rcon_o = (4); +end + 4: +begin +rcon_o = (8); +end + 5: +begin +rcon_o = ('h10); +end + 6: +begin +rcon_o = ('h20); +end + 7: +begin +rcon_o = ('h40); +end + 8: +begin +rcon_o = ('h80); +end + 9: +begin +rcon_o = ('h1B); +end + 10: +begin +rcon_o = ('h36); +end +default: +begin + rcon_o = (0); +end + endcase + + +end +//registers: +always @(posedge clk or negedge reset) + +begin + + if(!reset) + begin + state = (0); + col = (0); + key_reg = (0); + ready_o = (0); + end +else + begin + state = (next_state); + col = (next_col); + key_reg = (next_key_reg); + ready_o = (next_ready_o); + end + + +end +//generate_key: +reg[127:0] K_var,W_var; + reg[31:0] col_t; + reg[23:0] zero; + +always @( start_i or last_key_i or sbox_data_i or state or rcon_o or col or key_reg) + +begin + + + zero=0; + + col_t=col; + W_var=0; + + next_state = (state); + next_col = (col); + + next_ready_o = (0); + next_key_reg = (key_reg); + new_key_o = (key_reg); + +sbox_decrypt_o = (0); + sbox_access_o = (0); + sbox_data_o = (0); + K_var=last_key_i; + + case(state) + //Substitutethebyteswhilerotatingthem + //FouraccessestoSBoxareneeded + 0: +begin + if(start_i) +begin + + col_t=0; + sbox_access_o = (1); + sbox_data_o = (K_var[31:24]); + next_state = (1); + +end + + end + 1: +begin + sbox_access_o = (1); + sbox_data_o = (K_var[23:16]); + col_t[7:0]=sbox_data_i; + next_col = (col_t); + next_state = (2); + end + 2: +begin + sbox_access_o = (1); + sbox_data_o = (K_var[15:8]); + col_t[31:24]=sbox_data_i; + next_col = (col_t); + next_state = (3); + end + 3: +begin + sbox_access_o = (1); + sbox_data_o = (K_var[7:0]); + col_t[23:16]=sbox_data_i; + next_col = (col_t); + next_state = (4); + end + 4: +begin + sbox_access_o = (1); + col_t[15:8]=sbox_data_i; + next_col = (col_t); + W_var[127:96]=col_t^K_var[127:96]^{rcon_o,zero}; + W_var[95:64]=W_var[127:96]^K_var[95:64]; + W_var[63:32]=W_var[95:64]^K_var[63:32]; + W_var[31:0]=W_var[63:32]^K_var[31:0]; +next_ready_o = (1); +next_key_reg = (W_var); + next_state = (0); + end + +default: +begin + next_state = (0); + end +endcase + + +end + +endmodule diff --git a/tests/iwls2005/systemcaes/mixcolum.v b/tests/iwls2005/systemcaes/mixcolum.v new file mode 100644 index 000000000..ab6dc1e67 --- /dev/null +++ b/tests/iwls2005/systemcaes/mixcolum.v @@ -0,0 +1,188 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// Mixcolumns module implementation //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// Mixcolum module //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: mixcolum.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + + +module mixcolum(clk,reset,decrypt_i,start_i,data_i,ready_o,data_o); +input clk; +input reset; +input decrypt_i; +input start_i; +input [127:0] data_i; +output ready_o; +output [127:0] data_o; + +reg ready_o; +reg [127:0] data_o; + +reg [127:0] data_reg; +reg [127:0] next_data_reg; +reg [127:0] data_o_reg; +reg [127:0] next_data_o; +reg next_ready_o; +reg [1:0] state; +reg [1:0] next_state; +wire [31:0] outx; + +wire [31:0] outy; + +reg [31:0] mix_word; +reg [31:0] outmux; + +word_mixcolum w1 (.in(mix_word), .outx(outx), .outy(outy)); + +//assign_data_o: +always @( data_o_reg) + +begin + + data_o = (data_o_reg); + +end +//mux: +always @( outx or outy or decrypt_i) + +begin + + outmux = (decrypt_i?outy:outx); + +end +//registers: +always @(posedge clk or negedge reset) + +begin + +if(!reset) + begin + data_reg = (0); + state = (0); + ready_o = (0); + data_o_reg = (0); + end +else + begin + data_reg = (next_data_reg); + state = (next_state); + ready_o = (next_ready_o); + data_o_reg = (next_data_o); + end + + +end +//mixcol: +reg[127:0] data_i_var; + reg[31:0] aux; + reg[127:0] data_reg_var; + +always @( decrypt_i or start_i or state or data_reg or outmux or data_o_reg or data_i) + +begin + + + data_i_var=data_i; + data_reg_var=data_reg; + next_data_reg = (data_reg); + next_state = (state); + + mix_word = (0); + + next_ready_o = (0); + next_data_o = (data_o_reg); + + case(state) + + 0: +begin + if(start_i) +begin + + aux=data_i_var[127:96]; + mix_word = (aux); + data_reg_var[127:96]=outmux; + next_data_reg = (data_reg_var); + next_state = (1); + +end + + end + 1: +begin + aux=data_i_var[95:64]; + mix_word = (aux); + data_reg_var[95:64]=outmux; + next_data_reg = (data_reg_var); + next_state = (2); + end + 2: +begin + aux=data_i_var[63:32]; + mix_word = (aux); + data_reg_var[63:32]=outmux; + next_data_reg = (data_reg_var); + next_state = (3); + end + 3: +begin + aux=data_i_var[31:0]; + mix_word = (aux); + data_reg_var[31:0]=outmux; + next_data_o = (data_reg_var); + next_ready_o = (1); + next_state = (0); + end + default: + begin + end + endcase + + +end + +endmodule diff --git a/tests/iwls2005/systemcaes/sbox.v b/tests/iwls2005/systemcaes/sbox.v new file mode 100644 index 000000000..b5f741c30 --- /dev/null +++ b/tests/iwls2005/systemcaes/sbox.v @@ -0,0 +1,392 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// S-Box calculation //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// S-box calculation calculating inverse on gallois field //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: sbox.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + +module sbox(clk,reset,data_i,decrypt_i,data_o); +input clk; +input reset; +input [7:0] data_i; +input decrypt_i; +output [7:0] data_o; + +reg [7:0] data_o; + +reg [7:0] inva; +reg [3:0] ah; +reg [3:0] al; +reg [3:0] ah2; +reg [3:0] al2; +reg [3:0] alxh; +reg [3:0] alph; +reg [3:0] d; +reg [3:0] ahp; +reg [3:0] alp; +reg [3:0] to_invert; +reg [3:0] next_to_invert; +reg [3:0] ah_reg; +reg [3:0] next_ah_reg; +reg [3:0] next_alph; + + +//registers: +always @(posedge clk or negedge reset) + +begin + +if(!reset) +begin + +to_invert = (0); + ah_reg = (0); +alph = (0); + +end +else +begin + + to_invert = (next_to_invert); + ah_reg = (next_ah_reg); +alph = (next_alph); + +end + + +end +//first_mux: +reg[7:0] first_mux_data_var; + reg[7:0] first_mux_InvInput; + reg[3:0] first_mux_ah_t,first_mux_al_t; + reg first_mux_aA,first_mux_aB,first_mux_aC,first_mux_aD; + +always @( data_i or decrypt_i) + +begin + + + first_mux_data_var=data_i; + first_mux_InvInput=first_mux_data_var; + + case(decrypt_i) + 1: +begin + //Applyinverseaffinetrasformation +first_mux_aA=first_mux_data_var[0]^first_mux_data_var[5];first_mux_aB=first_mux_data_var[1]^first_mux_data_var[4]; + first_mux_aC=first_mux_data_var[2]^first_mux_data_var[7];first_mux_aD=first_mux_data_var[3]^first_mux_data_var[6]; + first_mux_InvInput[0]=(!first_mux_data_var[5])^first_mux_aC; + first_mux_InvInput[1]=first_mux_data_var[0]^first_mux_aD; + first_mux_InvInput[2]=(!first_mux_data_var[7])^first_mux_aB; + first_mux_InvInput[3]=first_mux_data_var[2]^first_mux_aA; + first_mux_InvInput[4]=first_mux_data_var[1]^first_mux_aD; + first_mux_InvInput[5]=first_mux_data_var[4]^first_mux_aC; + first_mux_InvInput[6]=first_mux_data_var[3]^first_mux_aA; + first_mux_InvInput[7]=first_mux_data_var[6]^first_mux_aB; + end + default: +begin +first_mux_InvInput=first_mux_data_var; + end + endcase + + + //ConvertelementsfromGF(2^8)intotwoelementsofGF(2^4^2) + + first_mux_aA=first_mux_InvInput[1]^first_mux_InvInput[7]; + first_mux_aB=first_mux_InvInput[5]^first_mux_InvInput[7]; + first_mux_aC=first_mux_InvInput[4]^first_mux_InvInput[6]; + + + first_mux_al_t[0]=first_mux_aC^first_mux_InvInput[0]^first_mux_InvInput[5]; + first_mux_al_t[1]=first_mux_InvInput[1]^first_mux_InvInput[2]; + first_mux_al_t[2]=first_mux_aA; + first_mux_al_t[3]=first_mux_InvInput[2]^first_mux_InvInput[4]; + + first_mux_ah_t[0]=first_mux_aC^first_mux_InvInput[5]; + first_mux_ah_t[1]=first_mux_aA^first_mux_aC; + first_mux_ah_t[2]=first_mux_aB^first_mux_InvInput[2]^first_mux_InvInput[3]; + first_mux_ah_t[3]=first_mux_aB; + + al = (first_mux_al_t); + ah = (first_mux_ah_t); + next_ah_reg = (first_mux_ah_t); + +end +//end_mux: +reg[7:0] end_mux_data_var,end_mux_data_o_var; + reg end_mux_aA,end_mux_aB,end_mux_aC,end_mux_aD; + +always @( decrypt_i or inva) + +begin + + + + //Taketheoutputoftheinverter + end_mux_data_var=inva; + + case(decrypt_i) + 0: +begin + //Applyaffinetrasformation +end_mux_aA=end_mux_data_var[0]^end_mux_data_var[1];end_mux_aB=end_mux_data_var[2]^end_mux_data_var[3]; + end_mux_aC=end_mux_data_var[4]^end_mux_data_var[5];end_mux_aD=end_mux_data_var[6]^end_mux_data_var[7]; + end_mux_data_o_var[0]=(!end_mux_data_var[0])^end_mux_aC^end_mux_aD; + end_mux_data_o_var[1]=(!end_mux_data_var[5])^end_mux_aA^end_mux_aD; + end_mux_data_o_var[2]=end_mux_data_var[2]^end_mux_aA^end_mux_aD; + end_mux_data_o_var[3]=end_mux_data_var[7]^end_mux_aA^end_mux_aB; + end_mux_data_o_var[4]=end_mux_data_var[4]^end_mux_aA^end_mux_aB; + end_mux_data_o_var[5]=(!end_mux_data_var[1])^end_mux_aB^end_mux_aC; + end_mux_data_o_var[6]=(!end_mux_data_var[6])^end_mux_aB^end_mux_aC; + end_mux_data_o_var[7]=end_mux_data_var[3]^end_mux_aC^end_mux_aD; + data_o = (end_mux_data_o_var); + end + default: +begin +data_o = (end_mux_data_var); + end + endcase + + + +end +//inversemap: +reg[3:0] aA,aB; + reg[3:0] inversemap_alp_t,inversemap_ahp_t; + reg[7:0] inversemap_inva_t; + +always @( alp or ahp) +begin + + + inversemap_alp_t=alp; + inversemap_ahp_t=ahp; + + aA=inversemap_alp_t[1]^inversemap_ahp_t[3]; + aB=inversemap_ahp_t[0]^inversemap_ahp_t[1]; + + inversemap_inva_t[0]=inversemap_alp_t[0]^inversemap_ahp_t[0]; + inversemap_inva_t[1]=aB^inversemap_ahp_t[3]; + inversemap_inva_t[2]=aA^aB; + inversemap_inva_t[3]=aB^inversemap_alp_t[1]^inversemap_ahp_t[2]; + inversemap_inva_t[4]=aA^aB^inversemap_alp_t[3]; + inversemap_inva_t[5]=aB^inversemap_alp_t[2]; + inversemap_inva_t[6]=aA^inversemap_alp_t[2]^inversemap_alp_t[3]^inversemap_ahp_t[0]; + inversemap_inva_t[7]=aB^inversemap_alp_t[2]^inversemap_ahp_t[3]; + + inva = (inversemap_inva_t); + +end +//mul1: +reg[3:0] mul1_alxh_t; + reg[3:0] mul1_aA,mul1_a; + +always @( ah or al) + +begin + + //alxah + + mul1_aA=al[0]^al[3]; + mul1_a=al[2]^al[3]; + + mul1_alxh_t[0]=(al[0]&ah[0])^(al[3]&ah[1])^(al[2]&ah[2])^(al[1]&ah[3]); + mul1_alxh_t[1]=(al[1]&ah[0])^(mul1_aA&ah[1])^(mul1_a&ah[2])^((al[1]^al[2])&ah[3]); + mul1_alxh_t[2]=(al[2]&ah[0])^(al[1]&ah[1])^(mul1_aA&ah[2])^(mul1_a&ah[3]); + mul1_alxh_t[3]=(al[3]&ah[0])^(al[2]&ah[1])^(al[1]&ah[2])^(mul1_aA&ah[3]); + + alxh = (mul1_alxh_t); + +end +//mul2: +reg[3:0] mul2_ahp_t; + reg[3:0] mul2_aA,mul2_aB; + +always @( d or ah_reg) + +begin + + //ahxd + + mul2_aA=ah_reg[0]^ah_reg[3]; + mul2_aB=ah_reg[2]^ah_reg[3]; + + mul2_ahp_t[0]=(ah_reg[0]&d[0])^(ah_reg[3]&d[1])^(ah_reg[2]&d[2])^(ah_reg[1]&d[3]); + mul2_ahp_t[1]=(ah_reg[1]&d[0])^(mul2_aA&d[1])^(mul2_aB&d[2])^((ah_reg[1]^ah_reg[2])&d[3]); + mul2_ahp_t[2]=(ah_reg[2]&d[0])^(ah_reg[1]&d[1])^(mul2_aA&d[2])^(mul2_aB&d[3]); + mul2_ahp_t[3]=(ah_reg[3]&d[0])^(ah_reg[2]&d[1])^(ah_reg[1]&d[2])^(mul2_aA&d[3]); + + ahp = (mul2_ahp_t); + +end +//mul3: +reg[3:0] mul3_alp_t; + reg[3:0] mul3_aA,mul3_aB; + +always @( d or alph) + +begin + + //dxal + + mul3_aA=d[0]^d[3]; + mul3_aB=d[2]^d[3]; + + mul3_alp_t[0]=(d[0]&alph[0])^(d[3]&alph[1])^(d[2]&alph[2])^(d[1]&alph[3]); + mul3_alp_t[1]=(d[1]&alph[0])^(mul3_aA&alph[1])^(mul3_aB&alph[2])^((d[1]^d[2])&alph[3]); + mul3_alp_t[2]=(d[2]&alph[0])^(d[1]&alph[1])^(mul3_aA&alph[2])^(mul3_aB&alph[3]); + mul3_alp_t[3]=(d[3]&alph[0])^(d[2]&alph[1])^(d[1]&alph[2])^(mul3_aA&alph[3]); + + alp = (mul3_alp_t); + +end +//intermediate: +reg[3:0] intermediate_aA,intermediate_aB; + reg[3:0] intermediate_ah2e,intermediate_ah2epl2,intermediate_to_invert_var; + +always @( ah2 or al2 or alxh) + +begin + + + //ahsquareismultipliedwithe + intermediate_aA=ah2[0]^ah2[1]; + intermediate_aB=ah2[2]^ah2[3]; + intermediate_ah2e[0]=ah2[1]^intermediate_aB; + intermediate_ah2e[1]=intermediate_aA; + intermediate_ah2e[2]=intermediate_aA^ah2[2]; + intermediate_ah2e[3]=intermediate_aA^intermediate_aB; + + //Additionofintermediate_ah2eplusal2 + intermediate_ah2epl2[0]=intermediate_ah2e[0]^al2[0]; + intermediate_ah2epl2[1]=intermediate_ah2e[1]^al2[1]; + intermediate_ah2epl2[2]=intermediate_ah2e[2]^al2[2]; + intermediate_ah2epl2[3]=intermediate_ah2e[3]^al2[3]; + + //Additionoflastresultwiththeresultof(alxah) + intermediate_to_invert_var[0]=intermediate_ah2epl2[0]^alxh[0]; + intermediate_to_invert_var[1]=intermediate_ah2epl2[1]^alxh[1]; + intermediate_to_invert_var[2]=intermediate_ah2epl2[2]^alxh[2]; + intermediate_to_invert_var[3]=intermediate_ah2epl2[3]^alxh[3]; + +//Registers + next_to_invert = (intermediate_to_invert_var); + +end +//inversion: +reg[3:0] inversion_to_invert_var; + reg[3:0] inversion_aA,inversion_d_t; + +always @( to_invert) + +begin + + + inversion_to_invert_var=to_invert; + + //InverttheresultinGF(2^4) + inversion_aA=inversion_to_invert_var[1]^inversion_to_invert_var[2]^inversion_to_invert_var[3]^(inversion_to_invert_var[1]&inversion_to_invert_var[2]&inversion_to_invert_var[3]); + inversion_d_t[0]=inversion_aA^inversion_to_invert_var[0]^(inversion_to_invert_var[0]&inversion_to_invert_var[2])^(inversion_to_invert_var[1]&inversion_to_invert_var[2])^(inversion_to_invert_var[0]&inversion_to_invert_var[1]&inversion_to_invert_var[2]); + inversion_d_t[1]=(inversion_to_invert_var[0]&inversion_to_invert_var[1])^(inversion_to_invert_var[0]&inversion_to_invert_var[2])^(inversion_to_invert_var[1]&inversion_to_invert_var[2])^inversion_to_invert_var[3]^(inversion_to_invert_var[1]&inversion_to_invert_var[3])^(inversion_to_invert_var[0]&inversion_to_invert_var[1]&inversion_to_invert_var[3]); + inversion_d_t[2]=(inversion_to_invert_var[0]&inversion_to_invert_var[1])^inversion_to_invert_var[2]^(inversion_to_invert_var[0]&inversion_to_invert_var[2])^inversion_to_invert_var[3]^(inversion_to_invert_var[0]&inversion_to_invert_var[3])^(inversion_to_invert_var[0]&inversion_to_invert_var[2]&inversion_to_invert_var[3]); + inversion_d_t[3]=inversion_aA^(inversion_to_invert_var[0]&inversion_to_invert_var[3])^(inversion_to_invert_var[1]&inversion_to_invert_var[3])^(inversion_to_invert_var[2]&inversion_to_invert_var[3]); + + d = (inversion_d_t); + + +end +//sum1: +reg[3:0] sum1_alph_t; + +always @( ah or al) + +begin + + + sum1_alph_t[0]=al[0]^ah[0]; + sum1_alph_t[1]=al[1]^ah[1]; + sum1_alph_t[2]=al[2]^ah[2]; + sum1_alph_t[3]=al[3]^ah[3]; + + next_alph = (sum1_alph_t); + +end +//square1: +reg[3:0] square1_ah_t; + +always @( ah) + +begin + + + square1_ah_t[0]=ah[0]^ah[2]; + square1_ah_t[1]=ah[2]; + square1_ah_t[2]=ah[1]^ah[3]; + square1_ah_t[3]=ah[3]; + + ah2 = (square1_ah_t); + +end +//square2: +reg[3:0] square2_al_t; + +always @( al) + +begin + + + square2_al_t[0]=al[0]^al[2]; + square2_al_t[1]=al[2]; + square2_al_t[2]=al[1]^al[3]; + square2_al_t[3]=al[3]; + + al2 = (square2_al_t); + +end + +endmodule diff --git a/tests/iwls2005/systemcaes/subbytes.v b/tests/iwls2005/systemcaes/subbytes.v new file mode 100644 index 000000000..6c6bd20c8 --- /dev/null +++ b/tests/iwls2005/systemcaes/subbytes.v @@ -0,0 +1,259 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// Subbytes module implementation //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// Subbytes module implementation //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: subbytes.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + +module subbytes(clk,reset,start_i,decrypt_i,data_i,ready_o,data_o,sbox_data_o,sbox_data_i,sbox_decrypt_o); +input clk; +input reset; +input start_i; +input decrypt_i; +input [127:0] data_i; +output ready_o; +output [127:0] data_o; +output [7:0] sbox_data_o; +input [7:0] sbox_data_i; +output sbox_decrypt_o; + +reg ready_o; +reg [127:0] data_o; +reg [7:0] sbox_data_o; +reg sbox_decrypt_o; + +reg [4:0] state; +reg [4:0] next_state; +reg [127:0] data_reg; +reg [127:0] next_data_reg; +reg next_ready_o; + +`define assign_array_to_128 \ + data_reg_128[127:120]=data_reg_var[0]; \ + data_reg_128[119:112]=data_reg_var[1]; \ + data_reg_128[111:104]=data_reg_var[2]; \ + data_reg_128[103:96]=data_reg_var[3]; \ + data_reg_128[95:88]=data_reg_var[4]; \ + data_reg_128[87:80]=data_reg_var[5]; \ + data_reg_128[79:72]=data_reg_var[6]; \ + data_reg_128[71:64]=data_reg_var[7]; \ + data_reg_128[63:56]=data_reg_var[8]; \ + data_reg_128[55:48]=data_reg_var[9]; \ + data_reg_128[47:40]=data_reg_var[10]; \ + data_reg_128[39:32]=data_reg_var[11]; \ + data_reg_128[31:24]=data_reg_var[12]; \ + data_reg_128[23:16]=data_reg_var[13]; \ + data_reg_128[15:8]=data_reg_var[14]; \ + data_reg_128[7:0]=data_reg_var[15]; + +`define shift_array_to_128 \ + data_reg_128[127:120]=data_reg_var[0]; \ + data_reg_128[119:112]=data_reg_var[5]; \ + data_reg_128[111:104]=data_reg_var[10]; \ + data_reg_128[103:96]=data_reg_var[15]; \ + data_reg_128[95:88]=data_reg_var[4]; \ + data_reg_128[87:80]=data_reg_var[9]; \ + data_reg_128[79:72]=data_reg_var[14]; \ + data_reg_128[71:64]=data_reg_var[3]; \ + data_reg_128[63:56]=data_reg_var[8]; \ + data_reg_128[55:48]=data_reg_var[13]; \ + data_reg_128[47:40]=data_reg_var[2]; \ + data_reg_128[39:32]=data_reg_var[7]; \ + data_reg_128[31:24]=data_reg_var[12]; \ + data_reg_128[23:16]=data_reg_var[1]; \ + data_reg_128[15:8]=data_reg_var[6]; \ + data_reg_128[7:0]=data_reg_var[11]; + +`define invert_shift_array_to_128 \ + data_reg_128[127:120]=data_reg_var[0]; \ + data_reg_128[119:112]=data_reg_var[13]; \ + data_reg_128[111:104]=data_reg_var[10]; \ + data_reg_128[103:96]=data_reg_var[7]; \ + data_reg_128[95:88]=data_reg_var[4]; \ + data_reg_128[87:80]=data_reg_var[1]; \ + data_reg_128[79:72]=data_reg_var[14]; \ + data_reg_128[71:64]=data_reg_var[11]; \ + data_reg_128[63:56]=data_reg_var[8]; \ + data_reg_128[55:48]=data_reg_var[5]; \ + data_reg_128[47:40]=data_reg_var[2]; \ + data_reg_128[39:32]=data_reg_var[15]; \ + data_reg_128[31:24]=data_reg_var[12]; \ + data_reg_128[23:16]=data_reg_var[9]; \ + data_reg_128[15:8]=data_reg_var[6]; \ + data_reg_128[7:0]=data_reg_var[3]; + + +//registers: +always @(posedge clk or negedge reset) + +begin + +if(!reset) +begin + + data_reg = (0); + state = (0); + ready_o = (0); + +end +else +begin + + data_reg = (next_data_reg); + state = (next_state); + ready_o = (next_ready_o); + +end + + +end +//sub: +reg[127:0] data_i_var,data_reg_128; +reg[7:0] data_array[15:0],data_reg_var[15:0]; + +always @( decrypt_i or start_i or state or data_i or sbox_data_i or data_reg) + +begin + + + data_i_var=data_i; + + data_array[0]=data_i_var[127:120]; + data_array[1]=data_i_var[119:112]; + data_array[2]=data_i_var[111:104]; + data_array[3]=data_i_var[103:96]; + data_array[4]=data_i_var[95:88]; + data_array[5]=data_i_var[87:80]; + data_array[6]=data_i_var[79:72]; + data_array[7]=data_i_var[71:64]; + data_array[8]=data_i_var[63:56]; + data_array[9]=data_i_var[55:48]; + data_array[10]=data_i_var[47:40]; + data_array[11]=data_i_var[39:32]; + data_array[12]=data_i_var[31:24]; + data_array[13]=data_i_var[23:16]; + data_array[14]=data_i_var[15:8]; + data_array[15]=data_i_var[7:0]; + + data_reg_var[0]=data_reg[127:120]; + data_reg_var[1]=data_reg[119:112]; + data_reg_var[2]=data_reg[111:104]; + data_reg_var[3]=data_reg[103:96]; + data_reg_var[4]=data_reg[95:88]; + data_reg_var[5]=data_reg[87:80]; + data_reg_var[6]=data_reg[79:72]; + data_reg_var[7]=data_reg[71:64]; + data_reg_var[8]=data_reg[63:56]; + data_reg_var[9]=data_reg[55:48]; + data_reg_var[10]=data_reg[47:40]; + data_reg_var[11]=data_reg[39:32]; + data_reg_var[12]=data_reg[31:24]; + data_reg_var[13]=data_reg[23:16]; + data_reg_var[14]=data_reg[15:8]; + data_reg_var[15]=data_reg[7:0]; + + + sbox_decrypt_o = (decrypt_i); + sbox_data_o = (0); + next_state = (state); + next_data_reg = (data_reg); + + next_ready_o = (0); + data_o = (data_reg); + + case(state) + + 0: +begin + if(start_i) +begin + +sbox_data_o = (data_array[0]); + next_state = (1); + +end + + end + 16: +begin + data_reg_var[15]=sbox_data_i; + //Makeshiftrowsstage + case(decrypt_i) + 0: + begin + `shift_array_to_128 + end + 1: + begin + `invert_shift_array_to_128 + end + endcase + + next_data_reg = (data_reg_128); + next_ready_o = (1); + next_state = (0); + end + default: + begin + /* original version (causing troubles with synopsys formality): + sbox_data_o = (data_array[state]); + data_reg_var[state-1]=sbox_data_i; + improved version: */ + sbox_data_o = (data_array[state & 15]); + data_reg_var[(state-1) & 15]=sbox_data_i; + /* end of improved version */ + `assign_array_to_128 + next_data_reg = (data_reg_128); + next_state = (state+1); + end + +endcase + + +end + +endmodule diff --git a/tests/iwls2005/systemcaes/timescale.v b/tests/iwls2005/systemcaes/timescale.v new file mode 100644 index 000000000..ff9e265a8 --- /dev/null +++ b/tests/iwls2005/systemcaes/timescale.v @@ -0,0 +1 @@ +`timescale 1ns / 10ps diff --git a/tests/iwls2005/systemcaes/word_mixcolum.v b/tests/iwls2005/systemcaes/word_mixcolum.v new file mode 100644 index 000000000..9308ccc9b --- /dev/null +++ b/tests/iwls2005/systemcaes/word_mixcolum.v @@ -0,0 +1,124 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// Mixcolumns for a 16 bit word module implementation //// +//// //// +//// This file is part of the SystemC AES //// +//// //// +//// Description: //// +//// Mixcolum for a 16 bit word //// +//// //// +//// Generated automatically using SystemC to Verilog translator //// +//// //// +//// To Do: //// +//// - done //// +//// //// +//// Author(s): //// +//// - Javier Castillo, jcastilo@opencores.org //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 Authors and OPENCORES.ORG //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: word_mixcolum.v,v $ +// Revision 1.1.1.1 2004/07/05 09:46:23 jcastillo +// First import +// + +module word_mixcolum(in,outx,outy); +input [31:0] in; +output [31:0] outx; +output [31:0] outy; + +reg [31:0] outx; +reg [31:0] outy; + +reg [7:0] a; +reg [7:0] b; +reg [7:0] c; +reg [7:0] d; +wire [7:0] x1; + +wire [7:0] x2; + +wire [7:0] x3; + +wire [7:0] x4; + +wire [7:0] y1; + +wire [7:0] y2; + +wire [7:0] y3; + +wire [7:0] y4; + + +byte_mixcolum bm1 (.a(a), .b(b), .c(c), .d(d), .outx(x1), .outy(y1)); +byte_mixcolum bm2 (.a(b), .b(c), .c(d), .d(a), .outx(x2), .outy(y2)); +byte_mixcolum bm3 (.a(c), .b(d), .c(a), .d(b), .outx(x3), .outy(y3)); +byte_mixcolum bm4 (.a(d), .b(a), .c(b), .d(c), .outx(x4), .outy(y4)); + + + reg[31:0] in_var; + reg[31:0] outx_var,outy_var; +//split: +always @( in) + +begin + + + + in_var=in; + a = (in_var[31:24]); + b = (in_var[23:16]); + c = (in_var[15:8]); + d = (in_var[7:0]); + +end +//mix: +always @( x1 or x2 or x3 or x4 or y1 or y2 or y3 or y4) + +begin + + + + outx_var[31:24]=x1; + outx_var[23:16]=x2; + outx_var[15:8]=x3; + outx_var[7:0]=x4; + outy_var[31:24]=y1; + outy_var[23:16]=y2; + outy_var[15:8]=y3; + outy_var[7:0]=y4; + + outx = (outx_var); + outy = (outy_var); + +end + +endmodule diff --git a/tests/iwls2005/usb_phy/timescale.v b/tests/iwls2005/usb_phy/timescale.v new file mode 100644 index 000000000..ff9e265a8 --- /dev/null +++ b/tests/iwls2005/usb_phy/timescale.v @@ -0,0 +1 @@ +`timescale 1ns / 10ps diff --git a/tests/iwls2005/usb_phy/usb_phy.v b/tests/iwls2005/usb_phy/usb_phy.v new file mode 100644 index 000000000..4ee345add --- /dev/null +++ b/tests/iwls2005/usb_phy/usb_phy.v @@ -0,0 +1,184 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// USB 1.1 PHY //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/usb_phy/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: usb_phy.v,v 1.4 2003/10/21 05:58:40 rudi Exp $ +// +// $Date: 2003/10/21 05:58:40 $ +// $Revision: 1.4 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: usb_phy.v,v $ +// Revision 1.4 2003/10/21 05:58:40 rudi +// usb_rst is no longer or'ed with the incomming reset internally. +// Now usb_rst is simply an output, the application can decide how +// to utilize it. +// +// Revision 1.3 2003/10/19 17:40:13 rudi +// - Made core more robust against line noise +// - Added Error Checking and Reporting +// (See README.txt for more info) +// +// Revision 1.2 2002/09/16 16:06:37 rudi +// Changed top level name to be consistent ... +// +// Revision 1.1.1.1 2002/09/16 14:26:59 rudi +// Created Directory Structure +// +// +// +// +// +// +// +// + +`include "timescale.v" + +module usb_phy(clk, rst, phy_tx_mode, usb_rst, + + // Transciever Interface + txdp, txdn, txoe, + rxd, rxdp, rxdn, + + // UTMI Interface + DataOut_i, TxValid_i, TxReady_o, RxValid_o, + RxActive_o, RxError_o, DataIn_o, LineState_o + ); + +input clk; +input rst; +input phy_tx_mode; +output usb_rst; +output txdp, txdn, txoe; +input rxd, rxdp, rxdn; +input [7:0] DataOut_i; +input TxValid_i; +output TxReady_o; +output [7:0] DataIn_o; +output RxValid_o; +output RxActive_o; +output RxError_o; +output [1:0] LineState_o; + +/////////////////////////////////////////////////////////////////// +// +// Local Wires and Registers +// + +reg [4:0] rst_cnt; +reg usb_rst; +wire fs_ce; +wire rst; + +/////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +/////////////////////////////////////////////////////////////////// +// +// TX Phy +// + +usb_tx_phy i_tx_phy( + .clk( clk ), + .rst( rst ), + .fs_ce( fs_ce ), + .phy_mode( phy_tx_mode ), + + // Transciever Interface + .txdp( txdp ), + .txdn( txdn ), + .txoe( txoe ), + + // UTMI Interface + .DataOut_i( DataOut_i ), + .TxValid_i( TxValid_i ), + .TxReady_o( TxReady_o ) + ); + +/////////////////////////////////////////////////////////////////// +// +// RX Phy and DPLL +// + +usb_rx_phy i_rx_phy( + .clk( clk ), + .rst( rst ), + .fs_ce( fs_ce ), + + // Transciever Interface + .rxd( rxd ), + .rxdp( rxdp ), + .rxdn( rxdn ), + + // UTMI Interface + .DataIn_o( DataIn_o ), + .RxValid_o( RxValid_o ), + .RxActive_o( RxActive_o ), + .RxError_o( RxError_o ), + .RxEn_i( txoe ), + .LineState( LineState_o ) + ); + +/////////////////////////////////////////////////////////////////// +// +// Generate an USB Reset is we see SE0 for at least 2.5uS +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) rst_cnt <= 5'h0; + else + if(LineState_o != 2'h0) rst_cnt <= 5'h0; + else + if(!usb_rst && fs_ce) rst_cnt <= rst_cnt + 5'h1; + +always @(posedge clk) + usb_rst <= (rst_cnt == 5'h1f); + +endmodule + diff --git a/tests/iwls2005/usb_phy/usb_rx_phy.v b/tests/iwls2005/usb_phy/usb_rx_phy.v new file mode 100644 index 000000000..c0568fb7d --- /dev/null +++ b/tests/iwls2005/usb_phy/usb_rx_phy.v @@ -0,0 +1,452 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// USB 1.1 PHY //// +//// RX & DPLL //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/usb_phy/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: usb_rx_phy.v,v 1.5 2004/10/19 09:29:07 rudi Exp $ +// +// $Date: 2004/10/19 09:29:07 $ +// $Revision: 1.5 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: usb_rx_phy.v,v $ +// Revision 1.5 2004/10/19 09:29:07 rudi +// Fixed DPLL alignment in the rx_phy and bit stuffing errors in the tx_phy (if last bit bit was a stuff bit in a packet it was omitted). +// +// Revision 1.4 2003/12/02 04:56:00 rudi +// Fixed a bug reported by Karl C. Posch from Graz University of Technology. Thanks Karl ! +// +// Revision 1.3 2003/10/19 18:07:45 rudi +// - Fixed Sync Error to be only checked/generated during the sync phase +// +// Revision 1.2 2003/10/19 17:40:13 rudi +// - Made core more robust against line noise +// - Added Error Checking and Reporting +// (See README.txt for more info) +// +// Revision 1.1.1.1 2002/09/16 14:27:01 rudi +// Created Directory Structure +// +// +// +// +// +// +// +// + +`include "timescale.v" + +module usb_rx_phy( clk, rst, fs_ce, + + // Transciever Interface + rxd, rxdp, rxdn, + + // UTMI Interface + RxValid_o, RxActive_o, RxError_o, DataIn_o, + RxEn_i, LineState); + +input clk; +input rst; +output fs_ce; +input rxd, rxdp, rxdn; +output [7:0] DataIn_o; +output RxValid_o; +output RxActive_o; +output RxError_o; +input RxEn_i; +output [1:0] LineState; + +/////////////////////////////////////////////////////////////////// +// +// Local Wires and Registers +// + +reg rxd_s0, rxd_s1, rxd_s; +reg rxdp_s0, rxdp_s1, rxdp_s, rxdp_s_r; +reg rxdn_s0, rxdn_s1, rxdn_s, rxdn_s_r; +reg synced_d; +wire k, j, se0; +reg rxd_r; +reg rx_en; +reg rx_active; +reg [2:0] bit_cnt; +reg rx_valid1, rx_valid; +reg shift_en; +reg sd_r; +reg sd_nrzi; +reg [7:0] hold_reg; +wire drop_bit; // Indicates a stuffed bit +reg [2:0] one_cnt; + +reg [1:0] dpll_state, dpll_next_state; +reg fs_ce_d; +reg fs_ce; +wire change; +wire lock_en; +reg [2:0] fs_state, fs_next_state; +reg rx_valid_r; +reg sync_err_d, sync_err; +reg bit_stuff_err; +reg se0_r, byte_err; +reg se0_s; + +/////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +assign RxActive_o = rx_active; +assign RxValid_o = rx_valid; +assign RxError_o = sync_err | bit_stuff_err | byte_err; +assign DataIn_o = hold_reg; +assign LineState = {rxdn_s1, rxdp_s1}; + +always @(posedge clk) rx_en <= RxEn_i; +always @(posedge clk) sync_err <= !rx_active & sync_err_d; + +/////////////////////////////////////////////////////////////////// +// +// Synchronize Inputs +// + +// First synchronize to the local system clock to +// avoid metastability outside the sync block (*_s0). +// Then make sure we see the signal for at least two +// clock cycles stable to avoid glitches and noise + +always @(posedge clk) rxd_s0 <= rxd; +always @(posedge clk) rxd_s1 <= rxd_s0; +always @(posedge clk) // Avoid detecting Line Glitches and noise + if(rxd_s0 && rxd_s1) rxd_s <= 1'b1; + else + if(!rxd_s0 && !rxd_s1) rxd_s <= 1'b0; + +always @(posedge clk) rxdp_s0 <= rxdp; +always @(posedge clk) rxdp_s1 <= rxdp_s0; +always @(posedge clk) rxdp_s_r <= rxdp_s0 & rxdp_s1; +always @(posedge clk) rxdp_s <= (rxdp_s0 & rxdp_s1) | rxdp_s_r; // Avoid detecting Line Glitches and noise + +always @(posedge clk) rxdn_s0 <= rxdn; +always @(posedge clk) rxdn_s1 <= rxdn_s0; +always @(posedge clk) rxdn_s_r <= rxdn_s0 & rxdn_s1; +always @(posedge clk) rxdn_s <= (rxdn_s0 & rxdn_s1) | rxdn_s_r; // Avoid detecting Line Glitches and noise + +assign k = !rxdp_s & rxdn_s; +assign j = rxdp_s & !rxdn_s; +assign se0 = !rxdp_s & !rxdn_s; + +always @(posedge clk) if(fs_ce) se0_s <= se0; + +/////////////////////////////////////////////////////////////////// +// +// DPLL +// + +// This design uses a clock enable to do 12Mhz timing and not a +// real 12Mhz clock. Everything always runs at 48Mhz. We want to +// make sure however, that the clock enable is always exactly in +// the middle between two virtual 12Mhz rising edges. +// We monitor rxdp and rxdn for any changes and do the appropiate +// adjustments. +// In addition to the locking done in the dpll FSM, we adjust the +// final latch enable to compensate for various sync registers ... + +// Allow lockinf only when we are receiving +assign lock_en = rx_en; + +always @(posedge clk) rxd_r <= rxd_s; + +// Edge detector +assign change = rxd_r != rxd_s; + +// DPLL FSM +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) dpll_state <= 2'h1; + else dpll_state <= dpll_next_state; + +always @(dpll_state or lock_en or change) + begin + fs_ce_d = 1'b0; + case(dpll_state) // synopsys full_case parallel_case + 2'h0: + if(lock_en && change) dpll_next_state = 2'h0; + else dpll_next_state = 2'h1; + 2'h1:begin + fs_ce_d = 1'b1; + if(lock_en && change) dpll_next_state = 2'h3; + else dpll_next_state = 2'h2; + end + 2'h2: + if(lock_en && change) dpll_next_state = 2'h0; + else dpll_next_state = 2'h3; + 2'h3: + if(lock_en && change) dpll_next_state = 2'h0; + else dpll_next_state = 2'h0; + endcase + end + +// Compensate for sync registers at the input - allign full speed +// clock enable to be in the middle between two bit changes ... +reg fs_ce_r1, fs_ce_r2; + +always @(posedge clk) fs_ce_r1 <= fs_ce_d; +always @(posedge clk) fs_ce_r2 <= fs_ce_r1; +always @(posedge clk) fs_ce <= fs_ce_r2; + + +/////////////////////////////////////////////////////////////////// +// +// Find Sync Pattern FSM +// + +parameter FS_IDLE = 3'h0, + K1 = 3'h1, + J1 = 3'h2, + K2 = 3'h3, + J2 = 3'h4, + K3 = 3'h5, + J3 = 3'h6, + K4 = 3'h7; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) fs_state <= FS_IDLE; + else fs_state <= fs_next_state; + +always @(fs_state or fs_ce or k or j or rx_en or rx_active or se0 or se0_s) + begin + synced_d = 1'b0; + sync_err_d = 1'b0; + fs_next_state = fs_state; + if(fs_ce && !rx_active && !se0 && !se0_s) + case(fs_state) // synopsys full_case parallel_case + FS_IDLE: + begin + if(k && rx_en) fs_next_state = K1; + end + K1: + begin + if(j && rx_en) fs_next_state = J1; + else + begin + sync_err_d = 1'b1; + fs_next_state = FS_IDLE; + end + end + J1: + begin + if(k && rx_en) fs_next_state = K2; + else + begin + sync_err_d = 1'b1; + fs_next_state = FS_IDLE; + end + end + K2: + begin + if(j && rx_en) fs_next_state = J2; + else + begin + sync_err_d = 1'b1; + fs_next_state = FS_IDLE; + end + end + J2: + begin + if(k && rx_en) fs_next_state = K3; + else + begin + sync_err_d = 1'b1; + fs_next_state = FS_IDLE; + end + end + K3: + begin + if(j && rx_en) fs_next_state = J3; + else + if(k && rx_en) + begin + fs_next_state = FS_IDLE; // Allow missing first K-J + synced_d = 1'b1; + end + else + begin + sync_err_d = 1'b1; + fs_next_state = FS_IDLE; + end + end + J3: + begin + if(k && rx_en) fs_next_state = K4; + else + begin + sync_err_d = 1'b1; + fs_next_state = FS_IDLE; + end + end + K4: + begin + if(k) synced_d = 1'b1; + fs_next_state = FS_IDLE; + end + endcase + end + +/////////////////////////////////////////////////////////////////// +// +// Generate RxActive +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) rx_active <= 1'b0; + else + if(synced_d && rx_en) rx_active <= 1'b1; + else + if(se0 && rx_valid_r) rx_active <= 1'b0; + +always @(posedge clk) + if(rx_valid) rx_valid_r <= 1'b1; + else + if(fs_ce) rx_valid_r <= 1'b0; + +/////////////////////////////////////////////////////////////////// +// +// NRZI Decoder +// + +always @(posedge clk) + if(fs_ce) sd_r <= rxd_s; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) sd_nrzi <= 1'b0; + else + if(!rx_active) sd_nrzi <= 1'b1; + else + if(rx_active && fs_ce) sd_nrzi <= !(rxd_s ^ sd_r); + +/////////////////////////////////////////////////////////////////// +// +// Bit Stuff Detect +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) one_cnt <= 3'h0; + else + if(!shift_en) one_cnt <= 3'h0; + else + if(fs_ce) + begin + if(!sd_nrzi || drop_bit) one_cnt <= 3'h0; + else one_cnt <= one_cnt + 3'h1; + end + +assign drop_bit = (one_cnt==3'h6); + +always @(posedge clk) bit_stuff_err <= drop_bit & sd_nrzi & fs_ce & !se0 & rx_active; // Bit Stuff Error + +/////////////////////////////////////////////////////////////////// +// +// Serial => Parallel converter +// + +always @(posedge clk) + if(fs_ce) shift_en <= synced_d | rx_active; + +always @(posedge clk) + if(fs_ce && shift_en && !drop_bit) + hold_reg <= {sd_nrzi, hold_reg[7:1]}; + +/////////////////////////////////////////////////////////////////// +// +// Generate RxValid +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) bit_cnt <= 3'b0; + else + if(!shift_en) bit_cnt <= 3'h0; + else + if(fs_ce && !drop_bit) bit_cnt <= bit_cnt + 3'h1; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) rx_valid1 <= 1'b0; + else + if(fs_ce && !drop_bit && (bit_cnt==3'h7)) rx_valid1 <= 1'b1; + else + if(rx_valid1 && fs_ce && !drop_bit) rx_valid1 <= 1'b0; + +always @(posedge clk) rx_valid <= !drop_bit & rx_valid1 & fs_ce; + +always @(posedge clk) se0_r <= se0; + +always @(posedge clk) byte_err <= se0 & !se0_r & (|bit_cnt[2:1]) & rx_active; + +endmodule + diff --git a/tests/iwls2005/usb_phy/usb_tx_phy.v b/tests/iwls2005/usb_phy/usb_tx_phy.v new file mode 100644 index 000000000..7f61ffd3b --- /dev/null +++ b/tests/iwls2005/usb_phy/usb_tx_phy.v @@ -0,0 +1,465 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// USB 1.1 PHY //// +//// TX //// +//// //// +//// //// +//// Author: Rudolf Usselmann //// +//// rudi@asics.ws //// +//// //// +//// //// +//// Downloaded from: http://www.opencores.org/cores/usb_phy/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000-2002 Rudolf Usselmann //// +//// www.asics.ws //// +//// rudi@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: usb_tx_phy.v,v 1.4 2004/10/19 09:29:07 rudi Exp $ +// +// $Date: 2004/10/19 09:29:07 $ +// $Revision: 1.4 $ +// $Author: rudi $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: usb_tx_phy.v,v $ +// Revision 1.4 2004/10/19 09:29:07 rudi +// Fixed DPLL alignment in the rx_phy and bit stuffing errors in the tx_phy (if last bit bit was a stuff bit in a packet it was omitted). +// +// Revision 1.3 2003/10/21 05:58:41 rudi +// usb_rst is no longer or'ed with the incomming reset internally. +// Now usb_rst is simply an output, the application can decide how +// to utilize it. +// +// Revision 1.2 2003/10/19 17:40:13 rudi +// - Made core more robust against line noise +// - Added Error Checking and Reporting +// (See README.txt for more info) +// +// Revision 1.1.1.1 2002/09/16 14:27:02 rudi +// Created Directory Structure +// +// +// +// +// +// +// + +`include "timescale.v" + +module usb_tx_phy( + clk, rst, fs_ce, phy_mode, + + // Transciever Interface + txdp, txdn, txoe, + + // UTMI Interface + DataOut_i, TxValid_i, TxReady_o + ); + +input clk; +input rst; +input fs_ce; +input phy_mode; +output txdp, txdn, txoe; +input [7:0] DataOut_i; +input TxValid_i; +output TxReady_o; + +/////////////////////////////////////////////////////////////////// +// +// Local Wires and Registers +// + +parameter IDLE = 3'd0, + SOP = 3'h1, + DATA = 3'h2, + EOP1 = 3'h3, + EOP2 = 3'h4, + WAIT = 3'h5; + +reg TxReady_o; +reg [2:0] state, next_state; +reg tx_ready_d; +reg ld_sop_d; +reg ld_data_d; +reg ld_eop_d; +reg tx_ip; +reg tx_ip_sync; +reg [2:0] bit_cnt; +reg [7:0] hold_reg; +reg [7:0] hold_reg_d; + +reg sd_raw_o; +wire hold; +reg data_done; +reg sft_done; +reg sft_done_r; +wire sft_done_e; +reg ld_data; +wire eop_done; +reg [2:0] one_cnt; +wire stuff; +reg sd_bs_o; +reg sd_nrzi_o; +reg append_eop; +reg append_eop_sync1; +reg append_eop_sync2; +reg append_eop_sync3; +reg append_eop_sync4; +reg txdp, txdn; +reg txoe_r1, txoe_r2; +reg txoe; + +/////////////////////////////////////////////////////////////////// +// +// Misc Logic +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) TxReady_o <= 1'b0; + else TxReady_o <= tx_ready_d & TxValid_i; + +always @(posedge clk) ld_data <= ld_data_d; + +/////////////////////////////////////////////////////////////////// +// +// Transmit in progress indicator +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) tx_ip <= 1'b0; + else + if(ld_sop_d) tx_ip <= 1'b1; + else + if(eop_done) tx_ip <= 1'b0; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) tx_ip_sync <= 1'b0; + else + if(fs_ce) tx_ip_sync <= tx_ip; + +// data_done helps us to catch cases where TxValid drops due to +// packet end and then gets re-asserted as a new packet starts. +// We might not see this because we are still transmitting. +// data_done should solve those cases ... +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) data_done <= 1'b0; + else + if(TxValid_i && ! tx_ip) data_done <= 1'b1; + else + if(!TxValid_i) data_done <= 1'b0; + +/////////////////////////////////////////////////////////////////// +// +// Shift Register +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) bit_cnt <= 3'h0; + else + if(!tx_ip_sync) bit_cnt <= 3'h0; + else + if(fs_ce && !hold) bit_cnt <= bit_cnt + 3'h1; + +assign hold = stuff; + +always @(posedge clk) + if(!tx_ip_sync) sd_raw_o <= 1'b0; + else + case(bit_cnt) // synopsys full_case parallel_case + 3'h0: sd_raw_o <= hold_reg_d[0]; + 3'h1: sd_raw_o <= hold_reg_d[1]; + 3'h2: sd_raw_o <= hold_reg_d[2]; + 3'h3: sd_raw_o <= hold_reg_d[3]; + 3'h4: sd_raw_o <= hold_reg_d[4]; + 3'h5: sd_raw_o <= hold_reg_d[5]; + 3'h6: sd_raw_o <= hold_reg_d[6]; + 3'h7: sd_raw_o <= hold_reg_d[7]; + endcase + +always @(posedge clk) + sft_done <= !hold & (bit_cnt == 3'h7); + +always @(posedge clk) + sft_done_r <= sft_done; + +assign sft_done_e = sft_done & !sft_done_r; + +// Out Data Hold Register +always @(posedge clk) + if(ld_sop_d) hold_reg <= 8'h80; + else + if(ld_data) hold_reg <= DataOut_i; + +always @(posedge clk) hold_reg_d <= hold_reg; + +/////////////////////////////////////////////////////////////////// +// +// Bit Stuffer +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) one_cnt <= 3'h0; + else + if(!tx_ip_sync) one_cnt <= 3'h0; + else + if(fs_ce) + begin + if(!sd_raw_o || stuff) one_cnt <= 3'h0; + else one_cnt <= one_cnt + 3'h1; + end + +assign stuff = (one_cnt==3'h6); + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) sd_bs_o <= 1'h0; + else + if(fs_ce) sd_bs_o <= !tx_ip_sync ? 1'b0 : (stuff ? 1'b0 : sd_raw_o); + +/////////////////////////////////////////////////////////////////// +// +// NRZI Encoder +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) sd_nrzi_o <= 1'b1; + else + if(!tx_ip_sync || !txoe_r1) sd_nrzi_o <= 1'b1; + else + if(fs_ce) sd_nrzi_o <= sd_bs_o ? sd_nrzi_o : ~sd_nrzi_o; + +/////////////////////////////////////////////////////////////////// +// +// EOP append logic +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) append_eop <= 1'b0; + else + if(ld_eop_d) append_eop <= 1'b1; + else + if(append_eop_sync2) append_eop <= 1'b0; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) append_eop_sync1 <= 1'b0; + else + if(fs_ce) append_eop_sync1 <= append_eop; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) append_eop_sync2 <= 1'b0; + else + if(fs_ce) append_eop_sync2 <= append_eop_sync1; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) append_eop_sync3 <= 1'b0; + else + if(fs_ce) append_eop_sync3 <= append_eop_sync2 | + (append_eop_sync3 & !append_eop_sync4); // Make sure always 2 bit wide + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) append_eop_sync4 <= 1'b0; + else + if(fs_ce) append_eop_sync4 <= append_eop_sync3; + +assign eop_done = append_eop_sync3; + +/////////////////////////////////////////////////////////////////// +// +// Output Enable Logic +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) txoe_r1 <= 1'b0; + else + if(fs_ce) txoe_r1 <= tx_ip_sync; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) txoe_r2 <= 1'b0; + else + if(fs_ce) txoe_r2 <= txoe_r1; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) txoe <= 1'b1; + else + if(fs_ce) txoe <= !(txoe_r1 | txoe_r2); + +/////////////////////////////////////////////////////////////////// +// +// Output Registers +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) txdp <= 1'b1; + else + if(fs_ce) txdp <= phy_mode ? + (!append_eop_sync3 & sd_nrzi_o) : + sd_nrzi_o; + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) txdn <= 1'b0; + else + if(fs_ce) txdn <= phy_mode ? + (!append_eop_sync3 & ~sd_nrzi_o) : + append_eop_sync3; + +/////////////////////////////////////////////////////////////////// +// +// Tx Statemashine +// + +`ifdef USB_ASYNC_REST +always @(posedge clk or negedge rst) +`else +always @(posedge clk) +`endif + if(!rst) state <= IDLE; + else state <= next_state; + +always @(state or TxValid_i or data_done or sft_done_e or eop_done or fs_ce) + begin + next_state = state; + tx_ready_d = 1'b0; + + ld_sop_d = 1'b0; + ld_data_d = 1'b0; + ld_eop_d = 1'b0; + + case(state) // synopsys full_case parallel_case + IDLE: + if(TxValid_i) + begin + ld_sop_d = 1'b1; + next_state = SOP; + end + SOP: + if(sft_done_e) + begin + tx_ready_d = 1'b1; + ld_data_d = 1'b1; + next_state = DATA; + end + DATA: + begin + if(!data_done && sft_done_e) + begin + ld_eop_d = 1'b1; + next_state = EOP1; + end + + if(data_done && sft_done_e) + begin + tx_ready_d = 1'b1; + ld_data_d = 1'b1; + end + end + EOP1: + if(eop_done) next_state = EOP2; + EOP2: + if(!eop_done && fs_ce) next_state = WAIT; + WAIT: + if(fs_ce) next_state = IDLE; + endcase + end + +endmodule + diff --git a/tests/no-icarus/README b/tests/no-icarus/README new file mode 100644 index 000000000..b43e7c02a --- /dev/null +++ b/tests/no-icarus/README @@ -0,0 +1,2 @@ +This directory contains test cases that can't be tested using Icarus Verilog +because they exceed the Verilog subset that is supported by Icarus Verilog. diff --git a/tests/no-icarus/autowire.v b/tests/no-icarus/autowire.v new file mode 100644 index 000000000..3633d4274 --- /dev/null +++ b/tests/no-icarus/autowire.v @@ -0,0 +1,25 @@ + +module test01(a, b, y); + +input [3:0] a, b; +output [3:0] y; + +assign temp1 = a + b; +assign temp2 = ~temp1; +assign y = temp2; + +endmodule + +// ------------------------------ + +module test02(a, b, y); + +input [3:0] a, b; +output [3:0] y; + +test01 test01_cell(A, B, Y); + +assign A = a, B = b, y = Y; + +endmodule + diff --git a/tests/no-icarus/var_range.v b/tests/no-icarus/var_range.v new file mode 100644 index 000000000..431eacb86 --- /dev/null +++ b/tests/no-icarus/var_range.v @@ -0,0 +1,45 @@ + +module test01(a, b, x, y, z); + +input [7:0] a; +input [2:0] b; +output [7:0] x, y; +output z; + +assign x = a >> b; +assign y = a[b+7:b]; +assign z = a[b]; + +endmodule + +module test02(clk, a, b, x, y, z); + +input clk; +input [7:0] a; +input [2:0] b; +output reg [7:0] x, y; +output reg z; + +always @(posedge clk) begin + x <= a >> b; + y <= a[b+7:b]; + z <= a[b]; +end + +endmodule + +module test03(clk, a, b, x, y); + +input clk; +input [2:0] a, b; +output reg [7:0] x; +output reg [9:0] y; + +always @(posedge clk) + y[b] <= a; + +always @(posedge clk) + y[b+2:b] <= a; + +endmodule + diff --git a/tests/openmsp430/rtl/omsp_alu.v b/tests/openmsp430/rtl/omsp_alu.v new file mode 100644 index 000000000..d1069973b --- /dev/null +++ b/tests/openmsp430/rtl/omsp_alu.v @@ -0,0 +1,258 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_alu.v +// +// *Module Description: +// openMSP430 ALU +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_alu ( + +// OUTPUTs + alu_out, // ALU output value + alu_out_add, // ALU adder output value + alu_stat, // ALU Status {V,N,Z,C} + alu_stat_wr, // ALU Status write {V,N,Z,C} + +// INPUTs + dbg_halt_st, // Halt/Run status from CPU + exec_cycle, // Instruction execution cycle + inst_alu, // ALU control signals + inst_bw, // Decoded Inst: byte width + inst_jmp, // Decoded Inst: Conditional jump + inst_so, // Single-operand arithmetic + op_dst, // Destination operand + op_src, // Source operand + status // R2 Status {V,N,Z,C} +); + +// OUTPUTs +//========= +output [15:0] alu_out; // ALU output value +output [15:0] alu_out_add; // ALU adder output value +output [3:0] alu_stat; // ALU Status {V,N,Z,C} +output [3:0] alu_stat_wr; // ALU Status write {V,N,Z,C} + +// INPUTs +//========= +input dbg_halt_st; // Halt/Run status from CPU +input exec_cycle; // Instruction execution cycle +input [11:0] inst_alu; // ALU control signals +input inst_bw; // Decoded Inst: byte width +input [7:0] inst_jmp; // Decoded Inst: Conditional jump +input [7:0] inst_so; // Single-operand arithmetic +input [15:0] op_dst; // Destination operand +input [15:0] op_src; // Source operand +input [3:0] status; // R2 Status {V,N,Z,C} + + +//============================================================================= +// 1) FUNCTIONS +//============================================================================= + +function [4:0] bcd_add; + + input [3:0] X; + input [3:0] Y; + input C_; + + reg [4:0] Z_; + begin + Z_ = {1'b0,X}+{1'b0,Y}+{4'b0,C_}; + if (Z_<5'd10) bcd_add = Z_; + else bcd_add = Z_+5'd6; + end + +endfunction + + +//============================================================================= +// 2) INSTRUCTION FETCH/DECODE CONTROL STATE MACHINE +//============================================================================= +// SINGLE-OPERAND ARITHMETIC: +//----------------------------------------------------------------------------- +// Mnemonic S-Reg, Operation Status bits +// D-Reg, V N Z C +// +// RRC dst C->MSB->...LSB->C * * * * +// RRA dst MSB->MSB->...LSB->C 0 * * * +// SWPB dst Swap bytes - - - - +// SXT dst Bit7->Bit8...Bit15 0 * * * +// PUSH src SP-2->SP, src->@SP - - - - +// CALL dst SP-2->SP, PC+2->@SP, dst->PC - - - - +// RETI TOS->SR, SP+2->SP, TOS->PC, SP+2->SP * * * * +// +//----------------------------------------------------------------------------- +// TWO-OPERAND ARITHMETIC: +//----------------------------------------------------------------------------- +// Mnemonic S-Reg, Operation Status bits +// D-Reg, V N Z C +// +// MOV src,dst src -> dst - - - - +// ADD src,dst src + dst -> dst * * * * +// ADDC src,dst src + dst + C -> dst * * * * +// SUB src,dst dst + ~src + 1 -> dst * * * * +// SUBC src,dst dst + ~src + C -> dst * * * * +// CMP src,dst dst + ~src + 1 * * * * +// DADD src,dst src + dst + C -> dst (decimaly) * * * * +// BIT src,dst src & dst 0 * * * +// BIC src,dst ~src & dst -> dst - - - - +// BIS src,dst src | dst -> dst - - - - +// XOR src,dst src ^ dst -> dst * * * * +// AND src,dst src & dst -> dst 0 * * * +// +//----------------------------------------------------------------------------- +// * the status bit is affected +// - the status bit is not affected +// 0 the status bit is cleared +// 1 the status bit is set +//----------------------------------------------------------------------------- + +// Invert source for substract and compare instructions. +wire op_src_inv_cmd = exec_cycle & (inst_alu[`ALU_SRC_INV]); +wire [15:0] op_src_inv = {16{op_src_inv_cmd}} ^ op_src; + + +// Mask the bit 8 for the Byte instructions for correct flags generation +wire op_bit8_msk = ~exec_cycle | ~inst_bw; +wire [16:0] op_src_in = {1'b0, {op_src_inv[15:8] & {8{op_bit8_msk}}}, op_src_inv[7:0]}; +wire [16:0] op_dst_in = {1'b0, {op_dst[15:8] & {8{op_bit8_msk}}}, op_dst[7:0]}; + +// Clear the source operand (= jump offset) for conditional jumps +wire jmp_not_taken = (inst_jmp[`JL] & ~(status[3]^status[2])) | + (inst_jmp[`JGE] & (status[3]^status[2])) | + (inst_jmp[`JN] & ~status[2]) | + (inst_jmp[`JC] & ~status[0]) | + (inst_jmp[`JNC] & status[0]) | + (inst_jmp[`JEQ] & ~status[1]) | + (inst_jmp[`JNE] & status[1]); +wire [16:0] op_src_in_jmp = op_src_in & {17{~jmp_not_taken}}; + +// Adder / AND / OR / XOR +wire [16:0] alu_add = op_src_in_jmp + op_dst_in; +wire [16:0] alu_and = op_src_in & op_dst_in; +wire [16:0] alu_or = op_src_in | op_dst_in; +wire [16:0] alu_xor = op_src_in ^ op_dst_in; + + +// Incrementer +wire alu_inc = exec_cycle & ((inst_alu[`ALU_INC_C] & status[0]) | + inst_alu[`ALU_INC]); +wire [16:0] alu_add_inc = alu_add + {16'h0000, alu_inc}; + + + +// Decimal adder (DADD) +wire [4:0] alu_dadd0 = bcd_add(op_src_in[3:0], op_dst_in[3:0], status[0]); +wire [4:0] alu_dadd1 = bcd_add(op_src_in[7:4], op_dst_in[7:4], alu_dadd0[4]); +wire [4:0] alu_dadd2 = bcd_add(op_src_in[11:8], op_dst_in[11:8], alu_dadd1[4]); +wire [4:0] alu_dadd3 = bcd_add(op_src_in[15:12], op_dst_in[15:12],alu_dadd2[4]); +wire [16:0] alu_dadd = {alu_dadd3, alu_dadd2[3:0], alu_dadd1[3:0], alu_dadd0[3:0]}; + + +// Shifter for rotate instructions (RRC & RRA) +wire alu_shift_msb = inst_so[`RRC] ? status[0] : + inst_bw ? op_src[7] : op_src[15]; +wire alu_shift_7 = inst_bw ? alu_shift_msb : op_src[8]; +wire [16:0] alu_shift = {1'b0, alu_shift_msb, op_src[15:9], alu_shift_7, op_src[7:1]}; + + +// Swap bytes / Extend Sign +wire [16:0] alu_swpb = {1'b0, op_src[7:0],op_src[15:8]}; +wire [16:0] alu_sxt = {1'b0, {8{op_src[7]}},op_src[7:0]}; + + +// Combine short paths toghether to simplify final ALU mux +wire alu_short_thro = ~(inst_alu[`ALU_AND] | + inst_alu[`ALU_OR] | + inst_alu[`ALU_XOR] | + inst_alu[`ALU_SHIFT] | + inst_so[`SWPB] | + inst_so[`SXT]); + +wire [16:0] alu_short = ({17{inst_alu[`ALU_AND]}} & alu_and) | + ({17{inst_alu[`ALU_OR]}} & alu_or) | + ({17{inst_alu[`ALU_XOR]}} & alu_xor) | + ({17{inst_alu[`ALU_SHIFT]}} & alu_shift) | + ({17{inst_so[`SWPB]}} & alu_swpb) | + ({17{inst_so[`SXT]}} & alu_sxt) | + ({17{alu_short_thro}} & op_src_in); + + +// ALU output mux +wire [16:0] alu_out_nxt = (inst_so[`IRQ] | dbg_halt_st | + inst_alu[`ALU_ADD]) ? alu_add_inc : + inst_alu[`ALU_DADD] ? alu_dadd : alu_short; + +assign alu_out = alu_out_nxt[15:0]; +assign alu_out_add = alu_add[15:0]; + + +//----------------------------------------------------------------------------- +// STATUS FLAG GENERATION +//----------------------------------------------------------------------------- + +wire V_xor = inst_bw ? (op_src_in[7] & op_dst_in[7]) : + (op_src_in[15] & op_dst_in[15]); + +wire V = inst_bw ? ((~op_src_in[7] & ~op_dst_in[7] & alu_out[7]) | + ( op_src_in[7] & op_dst_in[7] & ~alu_out[7])) : + ((~op_src_in[15] & ~op_dst_in[15] & alu_out[15]) | + ( op_src_in[15] & op_dst_in[15] & ~alu_out[15])); + +wire N = inst_bw ? alu_out[7] : alu_out[15]; +wire Z = inst_bw ? (alu_out[7:0]==0) : (alu_out==0); +wire C = inst_bw ? alu_out[8] : alu_out_nxt[16]; + +assign alu_stat = inst_alu[`ALU_SHIFT] ? {1'b0, N,Z,op_src_in[0]} : + inst_alu[`ALU_STAT_7] ? {1'b0, N,Z,~Z} : + inst_alu[`ALU_XOR] ? {V_xor,N,Z,~Z} : {V,N,Z,C}; + +assign alu_stat_wr = (inst_alu[`ALU_STAT_F] & exec_cycle) ? 4'b1111 : 4'b0000; + + +endmodule // omsp_alu + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_and_gate.v b/tests/openmsp430/rtl/omsp_and_gate.v new file mode 100644 index 000000000..7ceeb8d97 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_and_gate.v @@ -0,0 +1,89 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_and_gate.v +// +// *Module Description: +// Generic AND gate cell for the openMSP430 +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_and_gate ( + +// OUTPUTs + y, // AND gate output + +// INPUTs + a, // AND gate input A + b // AND gate input B +); + +// OUTPUTs +//========= +output y; // AND gate output + +// INPUTs +//========= +input a; // AND gate input A +input b; // AND gate input B + + +//============================================================================= +// 1) SOME COMMENTS ON THIS MODULE +//============================================================================= +// +// In its ASIC version, some combinatorial pathes of the openMSP430 are +// sensitive to glitches, in particular the ones generating the wakeup +// signals. +// To prevent synthesis from optmizing combinatorial clouds into glitchy +// logic, this AND gate module has been instanciated in the critical places. +// +// Make sure that synthesis doesn't ungroup this module. As an alternative, +// a standard cell from the library could also be directly instanciated here +// (don't forget the "dont_touch" attribute) +// +// +//============================================================================= +// 2) AND GATE +//============================================================================= + +assign y = a & b; + + +endmodule // omsp_and_gate + + + diff --git a/tests/openmsp430/rtl/omsp_clock_gate.v b/tests/openmsp430/rtl/omsp_clock_gate.v new file mode 100644 index 000000000..8ffe8e0d4 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_clock_gate.v @@ -0,0 +1,86 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_clock_gate.v +// +// *Module Description: +// Generic clock gate cell for the openMSP430 +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_clock_gate ( + +// OUTPUTs + gclk, // Gated clock + +// INPUTs + clk, // Clock + enable, // Clock enable + scan_enable // Scan enable (active during scan shifting) +); + +// OUTPUTs +//========= +output gclk; // Gated clock + +// INPUTs +//========= +input clk; // Clock +input enable; // Clock enable +input scan_enable; // Scan enable (active during scan shifting) + + +//============================================================================= +// CLOCK GATE: LATCH + AND +//============================================================================= + +// Enable clock gate during scan shift +// (the gate itself is checked with the scan capture cycle) +wire enable_in = (enable | scan_enable); + +// LATCH the enable signal +reg enable_latch; +always @(clk or enable_in) + if (~clk) + enable_latch <= enable_in; + +// AND gate +assign gclk = (clk & enable_latch); + + +endmodule // omsp_clock_gate + + diff --git a/tests/openmsp430/rtl/omsp_clock_module.v b/tests/openmsp430/rtl/omsp_clock_module.v new file mode 100644 index 000000000..670c5e590 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_clock_module.v @@ -0,0 +1,1058 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_clock_module.v +// +// *Module Description: +// Basic clock module implementation. +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_clock_module ( + +// OUTPUTs + aclk, // ACLK + aclk_en, // ACLK enable + cpu_en_s, // Enable CPU code execution (synchronous) + dbg_clk, // Debug unit clock + dbg_en_s, // Debug interface enable (synchronous) + dbg_rst, // Debug unit reset + dco_enable, // Fast oscillator enable + dco_wkup, // Fast oscillator wake-up (asynchronous) + lfxt_enable, // Low frequency oscillator enable + lfxt_wkup, // Low frequency oscillator wake-up (asynchronous) + mclk, // Main system clock + per_dout, // Peripheral data output + por, // Power-on reset + puc_pnd_set, // PUC pending set for the serial debug interface + puc_rst, // Main system reset + smclk, // SMCLK + smclk_en, // SMCLK enable + +// INPUTs + cpu_en, // Enable CPU code execution (asynchronous) + cpuoff, // Turns off the CPU + dbg_cpu_reset, // Reset CPU from debug interface + dbg_en, // Debug interface enable (asynchronous) + dco_clk, // Fast oscillator (fast clock) + lfxt_clk, // Low frequency oscillator (typ 32kHz) + mclk_enable, // Main System Clock enable + mclk_wkup, // Main System Clock wake-up (asynchronous) + oscoff, // Turns off LFXT1 clock input + per_addr, // Peripheral address + per_din, // Peripheral data input + per_en, // Peripheral enable (high active) + per_we, // Peripheral write enable (high active) + reset_n, // Reset Pin (low active, asynchronous) + scan_enable, // Scan enable (active during scan shifting) + scan_mode, // Scan mode + scg0, // System clock generator 1. Turns off the DCO + scg1, // System clock generator 1. Turns off the SMCLK + wdt_reset // Watchdog-timer reset +); + +// OUTPUTs +//========= +output aclk; // ACLK +output aclk_en; // ACLK enable +output cpu_en_s; // Enable CPU code execution (synchronous) +output dbg_clk; // Debug unit clock +output dbg_en_s; // Debug unit enable (synchronous) +output dbg_rst; // Debug unit reset +output dco_enable; // Fast oscillator enable +output dco_wkup; // Fast oscillator wake-up (asynchronous) +output lfxt_enable; // Low frequency oscillator enable +output lfxt_wkup; // Low frequency oscillator wake-up (asynchronous) +output mclk; // Main system clock +output [15:0] per_dout; // Peripheral data output +output por; // Power-on reset +output puc_pnd_set; // PUC pending set for the serial debug interface +output puc_rst; // Main system reset +output smclk; // SMCLK +output smclk_en; // SMCLK enable + +// INPUTs +//========= +input cpu_en; // Enable CPU code execution (asynchronous) +input cpuoff; // Turns off the CPU +input dbg_cpu_reset;// Reset CPU from debug interface +input dbg_en; // Debug interface enable (asynchronous) +input dco_clk; // Fast oscillator (fast clock) +input lfxt_clk; // Low frequency oscillator (typ 32kHz) +input mclk_enable; // Main System Clock enable +input mclk_wkup; // Main System Clock wake-up (asynchronous) +input oscoff; // Turns off LFXT1 clock input +input [13:0] per_addr; // Peripheral address +input [15:0] per_din; // Peripheral data input +input per_en; // Peripheral enable (high active) +input [1:0] per_we; // Peripheral write enable (high active) +input reset_n; // Reset Pin (low active, asynchronous) +input scan_enable; // Scan enable (active during scan shifting) +input scan_mode; // Scan mode +input scg0; // System clock generator 1. Turns off the DCO +input scg1; // System clock generator 1. Turns off the SMCLK +input wdt_reset; // Watchdog-timer reset + + +//============================================================================= +// 1) WIRES & PARAMETER DECLARATION +//============================================================================= + +// Register base address (must be aligned to decoder bit width) +parameter [14:0] BASE_ADDR = 15'h0050; + +// Decoder bit width (defines how many bits are considered for address decoding) +parameter DEC_WD = 4; + +// Register addresses offset +parameter [DEC_WD-1:0] BCSCTL1 = 'h7, + BCSCTL2 = 'h8; + +// Register one-hot decoder utilities +parameter DEC_SZ = (1 << DEC_WD); +parameter [DEC_SZ-1:0] BASE_REG = {{DEC_SZ-1{1'b0}}, 1'b1}; + +// Register one-hot decoder +parameter [DEC_SZ-1:0] BCSCTL1_D = (BASE_REG << BCSCTL1), + BCSCTL2_D = (BASE_REG << BCSCTL2); + +// Local wire declarations +wire nodiv_mclk; +wire nodiv_mclk_n; +wire nodiv_smclk; + + +//============================================================================ +// 2) REGISTER DECODER +//============================================================================ + +// Local register selection +wire reg_sel = per_en & (per_addr[13:DEC_WD-1]==BASE_ADDR[14:DEC_WD]); + +// Register local address +wire [DEC_WD-1:0] reg_addr = {1'b0, per_addr[DEC_WD-2:0]}; + +// Register address decode +wire [DEC_SZ-1:0] reg_dec = (BCSCTL1_D & {DEC_SZ{(reg_addr==(BCSCTL1 >>1))}}) | + (BCSCTL2_D & {DEC_SZ{(reg_addr==(BCSCTL2 >>1))}}); + +// Read/Write probes +wire reg_lo_write = per_we[0] & reg_sel; +wire reg_hi_write = per_we[1] & reg_sel; +wire reg_read = ~|per_we & reg_sel; + +// Read/Write vectors +wire [DEC_SZ-1:0] reg_hi_wr = reg_dec & {DEC_SZ{reg_hi_write}}; +wire [DEC_SZ-1:0] reg_lo_wr = reg_dec & {DEC_SZ{reg_lo_write}}; +wire [DEC_SZ-1:0] reg_rd = reg_dec & {DEC_SZ{reg_read}}; + + +//============================================================================ +// 3) REGISTERS +//============================================================================ + +// BCSCTL1 Register +//-------------- +reg [7:0] bcsctl1; +wire bcsctl1_wr = BCSCTL1[0] ? reg_hi_wr[BCSCTL1] : reg_lo_wr[BCSCTL1]; +wire [7:0] bcsctl1_nxt = BCSCTL1[0] ? per_din[15:8] : per_din[7:0]; + +`ifdef ASIC + `ifdef ACLK_DIVIDER +wire [7:0] divax_mask = 8'h30; + `else +wire [7:0] divax_mask = 8'h00; + `endif +`else +wire [7:0] divax_mask = 8'h30; +`endif + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) bcsctl1 <= 8'h00; + else if (bcsctl1_wr) bcsctl1 <= bcsctl1_nxt & divax_mask; // Mask unused bits + + +// BCSCTL2 Register +//-------------- +reg [7:0] bcsctl2; +wire bcsctl2_wr = BCSCTL2[0] ? reg_hi_wr[BCSCTL2] : reg_lo_wr[BCSCTL2]; +wire [7:0] bcsctl2_nxt = BCSCTL2[0] ? per_din[15:8] : per_din[7:0]; + +`ifdef MCLK_MUX +wire [7:0] selmx_mask = 8'h80; +`else +wire [7:0] selmx_mask = 8'h00; +`endif +`ifdef MCLK_DIVIDER +wire [7:0] divmx_mask = 8'h30; +`else +wire [7:0] divmx_mask = 8'h00; +`endif +`ifdef ASIC + `ifdef SMCLK_MUX +wire [7:0] sels_mask = 8'h08; + `else +wire [7:0] sels_mask = 8'h00; + `endif + `ifdef SMCLK_DIVIDER +wire [7:0] divsx_mask = 8'h06; + `else +wire [7:0] divsx_mask = 8'h00; + `endif +`else +wire [7:0] sels_mask = 8'h08; +wire [7:0] divsx_mask = 8'h06; +`endif + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) bcsctl2 <= 8'h00; + else if (bcsctl2_wr) bcsctl2 <= bcsctl2_nxt & ( sels_mask | divsx_mask | + selmx_mask | divmx_mask); // Mask unused bits + + +//============================================================================ +// 4) DATA OUTPUT GENERATION +//============================================================================ + +// Data output mux +wire [15:0] bcsctl1_rd = {8'h00, (bcsctl1 & {8{reg_rd[BCSCTL1]}})} << (8 & {4{BCSCTL1[0]}}); +wire [15:0] bcsctl2_rd = {8'h00, (bcsctl2 & {8{reg_rd[BCSCTL2]}})} << (8 & {4{BCSCTL2[0]}}); + +wire [15:0] per_dout = bcsctl1_rd | + bcsctl2_rd; + + +//============================================================================= +// 5) DCO_CLK / LFXT_CLK INTERFACES (WAKEUP, ENABLE, ...) +//============================================================================= + +`ifdef ASIC + wire cpuoff_and_mclk_enable; + omsp_and_gate and_cpuoff_mclk_en (.y(cpuoff_and_mclk_enable), .a(cpuoff), .b(mclk_enable)); +`endif + +//----------------------------------------------------------- +// 5.1) HIGH SPEED SYSTEM CLOCK GENERATOR (DCO_CLK) +//----------------------------------------------------------- +// Note1: switching off the DCO osillator is only +// supported in ASIC mode with SCG0 low power mode +// +// Note2: unlike the original MSP430 specification, +// we allow to switch off the DCO even +// if it is selected by MCLK or SMCLK. + +wire por_a; +wire dco_wkup; +wire cpu_en_wkup; + +`ifdef SCG0_EN + + // The DCO oscillator is synchronously disabled if: + // - the cpu pin is disabled (in that case, wait for mclk_enable==0) + // - the debug interface is disabled + // - SCG0 is set (in that case, wait for the mclk_enable==0 if selected by SELMx) + // + // Note that we make extensive use of the AND gate module in order + // to prevent glitch propagation on the wakeup logic cone. + wire cpu_enabled_with_dco; + wire dco_not_enabled_by_dbg; + wire dco_disable_by_scg0; + wire dco_disable_by_cpu_en; + wire dco_enable_nxt; + omsp_and_gate and_dco_dis1 (.y(cpu_enabled_with_dco), .a(~bcsctl2[`SELMx]), .b(cpuoff_and_mclk_enable)); + omsp_and_gate and_dco_dis2 (.y(dco_not_enabled_by_dbg), .a(~dbg_en_s), .b(~cpu_enabled_with_dco)); + omsp_and_gate and_dco_dis3 (.y(dco_disable_by_scg0), .a(scg0), .b(dco_not_enabled_by_dbg)); + omsp_and_gate and_dco_dis4 (.y(dco_disable_by_cpu_en), .a(~cpu_en_s), .b(~mclk_enable)); + omsp_and_gate and_dco_dis5 (.y(dco_enable_nxt), .a(~dco_disable_by_scg0), .b(~dco_disable_by_cpu_en)); + + // Register to prevent glitch propagation + reg dco_disable; + always @(posedge nodiv_mclk_n or posedge por) + if (por) dco_disable <= 1'b1; + else dco_disable <= ~dco_enable_nxt; + + // Note that a synchronizer is required if the MCLK mux is included + wire dco_clk_n = ~dco_clk; + `ifdef MCLK_MUX + omsp_sync_cell sync_cell_dco_disable ( + .data_out (dco_enable), + .data_in (~dco_disable), + .clk (dco_clk_n), + .rst (por) + ); + `else + + assign dco_enable = ~dco_disable; + `endif + + // The DCO oscillator will get an asynchronous wakeup if: + // - the MCLK generates a wakeup (only if the MCLK mux selects dco_clk) + // - if the DCO wants to be synchronously enabled (i.e dco_enable_nxt=1) + wire dco_mclk_wkup; + wire dco_en_wkup; + omsp_and_gate and_dco_mclk_wkup (.y(dco_mclk_wkup), .a(mclk_wkup), .b(~bcsctl2[`SELMx])); + omsp_and_gate and_dco_en_wkup (.y(dco_en_wkup), .a(~dco_enable), .b(dco_enable_nxt)); + + wire dco_wkup_set = dco_mclk_wkup | dco_en_wkup | cpu_en_wkup; + + // Scan MUX for the asynchronous SET + wire dco_wkup_set_scan; + omsp_scan_mux scan_mux_dco_wkup ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (dco_wkup_set | por), + .data_out (dco_wkup_set_scan) + ); + + // Scan MUX to increase coverage + wire dco_wkup_clear; + omsp_scan_mux scan_mux_dco_wkup_clear ( + .scan_mode (scan_mode), + .data_in_scan (dco_wkup_set), + .data_in_func (1'b1), + .data_out (dco_wkup_clear) + ); + + // The wakeup is asynchronously set, synchronously released + wire dco_wkup_n; + omsp_sync_cell sync_cell_dco_wkup ( + .data_out (dco_wkup_n), + .data_in (dco_wkup_clear), + .clk (dco_clk_n), + .rst (dco_wkup_set_scan) + ); + + omsp_and_gate and_dco_wkup (.y(dco_wkup), .a(~dco_wkup_n), .b(cpu_en)); + +`else + assign dco_enable = 1'b1; + assign dco_wkup = 1'b1; +`endif + + +//----------------------------------------------------------- +// 5.2) LOW FREQUENCY CRYSTAL CLOCK GENERATOR (LFXT_CLK) +//----------------------------------------------------------- + +// ASIC MODE +//------------------------------------------------ +// Note: unlike the original MSP430 specification, +// we allow to switch off the LFXT even +// if it is selected by MCLK or SMCLK. +`ifdef ASIC + +`ifdef OSCOFF_EN + + // The LFXT is synchronously disabled if: + // - the cpu pin is disabled (in that case, wait for mclk_enable==0) + // - the debug interface is disabled + // - OSCOFF is set (in that case, wait for the mclk_enable==0 if selected by SELMx) + wire cpu_enabled_with_lfxt; + wire lfxt_not_enabled_by_dbg; + wire lfxt_disable_by_oscoff; + wire lfxt_disable_by_cpu_en; + wire lfxt_enable_nxt; + omsp_and_gate and_lfxt_dis1 (.y(cpu_enabled_with_lfxt), .a(bcsctl2[`SELMx]), .b(cpuoff_and_mclk_enable)); + omsp_and_gate and_lfxt_dis2 (.y(lfxt_not_enabled_by_dbg), .a(~dbg_en_s), .b(~cpu_enabled_with_lfxt)); + omsp_and_gate and_lfxt_dis3 (.y(lfxt_disable_by_oscoff), .a(oscoff), .b(lfxt_not_enabled_by_dbg)); + omsp_and_gate and_lfxt_dis4 (.y(lfxt_disable_by_cpu_en), .a(~cpu_en_s), .b(~mclk_enable)); + omsp_and_gate and_lfxt_dis5 (.y(lfxt_enable_nxt), .a(~lfxt_disable_by_oscoff), .b(~lfxt_disable_by_cpu_en)); + + // Register to prevent glitch propagation + reg lfxt_disable; + always @(posedge nodiv_mclk_n or posedge por) + if (por) lfxt_disable <= 1'b1; + else lfxt_disable <= ~lfxt_enable_nxt; + + // Synchronize the OSCOFF control signal to the LFXT clock domain + wire lfxt_clk_n = ~lfxt_clk; + omsp_sync_cell sync_cell_lfxt_disable ( + .data_out (lfxt_enable), + .data_in (~lfxt_disable), + .clk (lfxt_clk_n), + .rst (por) + ); + + // The LFXT will get an asynchronous wakeup if: + // - the MCLK generates a wakeup (only if the MCLK mux selects lfxt_clk) + // - if the LFXT wants to be synchronously enabled (i.e lfxt_enable_nxt=1) + wire lfxt_mclk_wkup; + wire lfxt_en_wkup; + omsp_and_gate and_lfxt_mclk_wkup (.y(lfxt_mclk_wkup), .a(mclk_wkup), .b(bcsctl2[`SELMx])); + omsp_and_gate and_lfxt_en_wkup (.y(lfxt_en_wkup), .a(~lfxt_enable), .b(lfxt_enable_nxt)); + + wire lfxt_wkup_set = lfxt_mclk_wkup | lfxt_en_wkup | cpu_en_wkup; + + // Scan MUX for the asynchronous SET + wire lfxt_wkup_set_scan; + omsp_scan_mux scan_mux_lfxt_wkup ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (lfxt_wkup_set | por), + .data_out (lfxt_wkup_set_scan) + ); + + // Scan MUX to increase coverage + wire lfxt_wkup_clear; + omsp_scan_mux scan_mux_lfxt_wkup_clear ( + .scan_mode (scan_mode), + .data_in_scan (lfxt_wkup_set), + .data_in_func (1'b1), + .data_out (lfxt_wkup_clear) + ); + + // The wakeup is asynchronously set, synchronously released + wire lfxt_wkup_n; + omsp_sync_cell sync_cell_lfxt_wkup ( + .data_out (lfxt_wkup_n), + .data_in (lfxt_wkup_clear), + .clk (lfxt_clk_n), + .rst (lfxt_wkup_set_scan) + ); + + omsp_and_gate and_lfxt_wkup (.y(lfxt_wkup), .a(~lfxt_wkup_n), .b(cpu_en)); + +`else + assign lfxt_enable = 1'b1; + assign lfxt_wkup = 1'b0; +`endif + + +// FPGA MODE +//--------------------------------------- +// Synchronize LFXT_CLK & edge detection +`else + +wire lfxt_clk_s; + +omsp_sync_cell sync_cell_lfxt_clk ( + .data_out (lfxt_clk_s), + .data_in (lfxt_clk), + .clk (mclk), + .rst (por) +); + +reg lfxt_clk_dly; + +always @ (posedge mclk or posedge por) + if (por) lfxt_clk_dly <= 1'b0; + else lfxt_clk_dly <= lfxt_clk_s; + +wire lfxt_clk_en = (lfxt_clk_s & ~lfxt_clk_dly) & ~(oscoff & ~bcsctl2[`SELS]); +assign lfxt_enable = 1'b1; +assign lfxt_wkup = 1'b0; +`endif + + +//============================================================================= +// 6) CLOCK GENERATION +//============================================================================= + +//----------------------------------------------------------- +// 6.1) GLOBAL CPU ENABLE +//----------------------------------------------------------- +// ACLK and SMCLK are directly switched-off +// with the cpu_en pin (after synchronization). +// MCLK will be switched off once the CPU reaches +// its IDLE state (through the mclk_enable signal) + + +// Synchronize CPU_EN signal to the MCLK domain +//---------------------------------------------- +`ifdef SYNC_CPU_EN + omsp_sync_cell sync_cell_cpu_en ( + .data_out (cpu_en_s), + .data_in (cpu_en), + .clk (nodiv_mclk), + .rst (por) + ); + omsp_and_gate and_cpu_en_wkup (.y(cpu_en_wkup), .a(cpu_en), .b(~cpu_en_s)); +`else + assign cpu_en_s = cpu_en; + assign cpu_en_wkup = 1'b0; +`endif + +// Synchronize CPU_EN signal to the ACLK domain +//---------------------------------------------- +`ifdef LFXT_DOMAIN + wire cpu_en_aux_s; + omsp_sync_cell sync_cell_cpu_aux_en ( + .data_out (cpu_en_aux_s), + .data_in (cpu_en), + .clk (lfxt_clk), + .rst (por) + ); +`else + wire cpu_en_aux_s = cpu_en_s; +`endif + +// Synchronize CPU_EN signal to the SMCLK domain +//---------------------------------------------- +// Note: the synchronizer is only required if there is a SMCLK_MUX +`ifdef ASIC + `ifdef SMCLK_MUX + wire cpu_en_sm_s; + omsp_sync_cell sync_cell_cpu_sm_en ( + .data_out (cpu_en_sm_s), + .data_in (cpu_en), + .clk (nodiv_smclk), + .rst (por) + ); + `else + wire cpu_en_sm_s = cpu_en_s; + `endif +`endif + + +//----------------------------------------------------------- +// 6.2) MCLK GENERATION +//----------------------------------------------------------- + +// Clock MUX +//---------------------------- +`ifdef MCLK_MUX +omsp_clock_mux clock_mux_mclk ( + .clk_out (nodiv_mclk), + .clk_in0 (dco_clk), + .clk_in1 (lfxt_clk), + .reset (por), + .scan_mode (scan_mode), + .select (bcsctl2[`SELMx]) +); +`else +assign nodiv_mclk = dco_clk; +`endif +assign nodiv_mclk_n = ~nodiv_mclk; + + +// Wakeup synchronizer +//---------------------------- +wire mclk_wkup_s; + +`ifdef CPUOFF_EN +omsp_sync_cell sync_cell_mclk_wkup ( + .data_out (mclk_wkup_s), + .data_in (mclk_wkup), + .clk (nodiv_mclk), + .rst (puc_rst) +); +`else + assign mclk_wkup_s = 1'b0; +`endif + + +// Clock Divider +//---------------------------- +// No need for extra synchronizer as bcsctl2 +// comes from the same clock domain. + +`ifdef CPUOFF_EN +wire mclk_active = mclk_enable | mclk_wkup_s | (dbg_en_s & cpu_en_s); +`else +wire mclk_active = 1'b1; +`endif + +`ifdef MCLK_DIVIDER +reg [2:0] mclk_div; +always @ (posedge nodiv_mclk or posedge puc_rst) + if (puc_rst) mclk_div <= 3'h0; + else if ((bcsctl2[`DIVMx]!=2'b00)) mclk_div <= mclk_div+3'h1; + + wire mclk_div_en = mclk_active & ((bcsctl2[`DIVMx]==2'b00) ? 1'b1 : + (bcsctl2[`DIVMx]==2'b01) ? mclk_div[0] : + (bcsctl2[`DIVMx]==2'b10) ? &mclk_div[1:0] : + &mclk_div[2:0]); +`else + wire mclk_div_en = mclk_active; +`endif + + +// Generate main system clock +//---------------------------- +`ifdef MCLK_CGATE + +omsp_clock_gate clock_gate_mclk ( + .gclk (mclk), + .clk (nodiv_mclk), + .enable (mclk_div_en), + .scan_enable (scan_enable) +); +`else + assign mclk = nodiv_mclk; +`endif + + +//----------------------------------------------------------- +// 6.3) ACLK GENERATION +//----------------------------------------------------------- + +// ASIC MODE +//---------------------------- +`ifdef ASIC + + `ifdef ACLK_DIVIDER + `ifdef LFXT_DOMAIN + + wire nodiv_aclk = lfxt_clk; + + // Local Reset synchronizer + wire puc_lfxt_rst; + wire puc_lfxt_noscan_n; + omsp_sync_cell sync_cell_puc_lfxt ( + .data_out (puc_lfxt_noscan_n), + .data_in (1'b1), + .clk (nodiv_aclk), + .rst (puc_rst) + ); + omsp_scan_mux scan_mux_puc_lfxt ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (~puc_lfxt_noscan_n), + .data_out (puc_lfxt_rst) + ); + + // Local synchronizer for the bcsctl1.DIVAx configuration + // (note that we can live with a full bus synchronizer as + // it won't hurt if we get a wrong DIVAx value for a single clock cycle) + reg [1:0] divax_s; + reg [1:0] divax_ss; + always @ (posedge nodiv_aclk or posedge puc_lfxt_rst) + if (puc_lfxt_rst) + begin + divax_s <= 2'h0; + divax_ss <= 2'h0; + end + else + begin + divax_s <= bcsctl1[`DIVAx]; + divax_ss <= divax_s; + end + + // If the OSCOFF mode is enabled synchronize OSCOFF signal + wire oscoff_s; + `ifdef OSCOFF_EN + omsp_sync_cell sync_cell_oscoff ( + .data_out (oscoff_s), + .data_in (oscoff), + .clk (nodiv_aclk), + .rst (puc_lfxt_rst) + ); + `else + assign oscoff_s = 1'b0; + `endif + `else + wire puc_lfxt_rst = puc_rst; + wire nodiv_aclk = dco_clk; + wire [1:0] divax_ss = bcsctl1[`DIVAx]; + wire oscoff_s = oscoff; + `endif + + // Divider + reg [2:0] aclk_div; + always @ (posedge nodiv_aclk or posedge puc_lfxt_rst) + if (puc_lfxt_rst) aclk_div <= 3'h0; + else if ((divax_ss!=2'b00)) aclk_div <= aclk_div+3'h1; + + wire aclk_div_en = cpu_en_aux_s & ~oscoff_s & ((divax_ss==2'b00) ? 1'b1 : + (divax_ss==2'b01) ? aclk_div[0] : + (divax_ss==2'b10) ? &aclk_div[1:0] : + &aclk_div[2:0]); + + // Clock gate + omsp_clock_gate clock_gate_aclk ( + .gclk (aclk), + .clk (nodiv_aclk), + .enable (aclk_div_en), + .scan_enable (scan_enable) + ); + + `else + `ifdef LFXT_DOMAIN + assign aclk = lfxt_clk; + `else + assign aclk = dco_clk; + `endif + `endif + + + assign aclk_en = 1'b1; + + +// FPGA MODE +//---------------------------- +`else + reg aclk_en; + reg [2:0] aclk_div; + wire aclk_en_nxt = lfxt_clk_en & ((bcsctl1[`DIVAx]==2'b00) ? 1'b1 : + (bcsctl1[`DIVAx]==2'b01) ? aclk_div[0] : + (bcsctl1[`DIVAx]==2'b10) ? &aclk_div[1:0] : + &aclk_div[2:0]); + + always @ (posedge mclk or posedge puc_rst) + if (puc_rst) aclk_div <= 3'h0; + else if ((bcsctl1[`DIVAx]!=2'b00) & lfxt_clk_en) aclk_div <= aclk_div+3'h1; + + always @ (posedge mclk or posedge puc_rst) + if (puc_rst) aclk_en <= 1'b0; + else aclk_en <= aclk_en_nxt & cpu_en_s; + + assign aclk = mclk; +`endif + +//----------------------------------------------------------- +// 6.4) SMCLK GENERATION +//----------------------------------------------------------- + +// Clock MUX +//---------------------------- +`ifdef SMCLK_MUX +omsp_clock_mux clock_mux_smclk ( + .clk_out (nodiv_smclk), + .clk_in0 (dco_clk), + .clk_in1 (lfxt_clk), + .reset (por), + .scan_mode (scan_mode), + .select (bcsctl2[`SELS]) +); +`else +assign nodiv_smclk = dco_clk; +`endif + + +// ASIC MODE +//---------------------------- +`ifdef ASIC + `ifdef SMCLK_MUX + + // Synchronizers + //------------------------------------------------------ + // When the SMCLK MUX is enabled, the reset and DIVSx + // and SCG1 signals must be synchronized, otherwise not. + + // Local Reset synchronizer + wire puc_sm_noscan_n; + wire puc_sm_rst; + omsp_sync_cell sync_cell_puc_sm ( + .data_out (puc_sm_noscan_n), + .data_in (1'b1), + .clk (nodiv_smclk), + .rst (puc_rst) + ); + omsp_scan_mux scan_mux_puc_sm ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (~puc_sm_noscan_n), + .data_out (puc_sm_rst) + ); + + // SCG1 synchronizer + `ifdef SCG1_EN + wire scg1_s; + omsp_sync_cell sync_cell_scg1 ( + .data_out (scg1_s), + .data_in (scg1), + .clk (nodiv_smclk), + .rst (puc_sm_rst) + ); + `else + wire scg1_s = 1'b0; + `endif + + `ifdef SMCLK_DIVIDER + // Local synchronizer for the bcsctl2.DIVSx configuration + // (note that we can live with a full bus synchronizer as + // it won't hurt if we get a wrong DIVSx value for a single clock cycle) + reg [1:0] divsx_s; + reg [1:0] divsx_ss; + always @ (posedge nodiv_smclk or posedge puc_sm_rst) + if (puc_sm_rst) + begin + divsx_s <= 2'h0; + divsx_ss <= 2'h0; + end + else + begin + divsx_s <= bcsctl2[`DIVSx]; + divsx_ss <= divsx_s; + end + `endif + + `else + + wire puc_sm_rst = puc_rst; + wire [1:0] divsx_ss = bcsctl2[`DIVSx]; + wire scg1_s = scg1; + `endif + + + + // Clock Divider + //---------------------------- + `ifdef SMCLK_DIVIDER + + reg [2:0] smclk_div; + always @ (posedge nodiv_smclk or posedge puc_sm_rst) + if (puc_sm_rst) smclk_div <= 3'h0; + else if ((divsx_ss!=2'b00)) smclk_div <= smclk_div+3'h1; + + wire smclk_div_en = cpu_en_sm_s & ~scg1_s & ((divsx_ss==2'b00) ? 1'b1 : + (divsx_ss==2'b01) ? smclk_div[0] : + (divsx_ss==2'b10) ? &smclk_div[1:0] : + &smclk_div[2:0]); + `else + `ifdef SCG1_EN + wire smclk_div_en = cpu_en_sm_s & ~scg1_s; + `else + wire smclk_div_en = cpu_en_sm_s; + `endif + `endif + + + // Generate sub-system clock + //---------------------------- + `ifdef SMCLK_CGATE + omsp_clock_gate clock_gate_smclk ( + .gclk (smclk), + .clk (nodiv_smclk), + .enable (smclk_div_en), + .scan_enable (scan_enable) + ); + `else + assign smclk = nodiv_smclk; + `endif + + assign smclk_en = 1'b1; + + +// FPGA MODE +//---------------------------- +`else +reg smclk_en; +reg [2:0] smclk_div; + +wire smclk_in = ~scg1 & (bcsctl2[`SELS] ? lfxt_clk_en : 1'b1); + +wire smclk_en_nxt = smclk_in & ((bcsctl2[`DIVSx]==2'b00) ? 1'b1 : + (bcsctl2[`DIVSx]==2'b01) ? smclk_div[0] : + (bcsctl2[`DIVSx]==2'b10) ? &smclk_div[1:0] : + &smclk_div[2:0]); + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) smclk_en <= 1'b0; + else smclk_en <= smclk_en_nxt & cpu_en_s; + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) smclk_div <= 3'h0; + else if ((bcsctl2[`DIVSx]!=2'b00) & smclk_in) smclk_div <= smclk_div+3'h1; + +wire smclk = mclk; + +`endif + +//----------------------------------------------------------- +// 6.5) DEBUG INTERFACE CLOCK GENERATION (DBG_CLK) +//----------------------------------------------------------- + +// Synchronize DBG_EN signal to MCLK domain +//------------------------------------------ +`ifdef DBG_EN +`ifdef SYNC_DBG_EN + wire dbg_en_n_s; + omsp_sync_cell sync_cell_dbg_en ( + .data_out (dbg_en_n_s), + .data_in (~dbg_en), + .clk (mclk), + .rst (por) + ); + assign dbg_en_s = ~dbg_en_n_s; + wire dbg_rst_nxt = dbg_en_n_s; +`else + assign dbg_en_s = dbg_en; + wire dbg_rst_nxt = ~dbg_en; +`endif +`else + assign dbg_en_s = 1'b0; + wire dbg_rst_nxt = 1'b0; +`endif + + +// Serial Debug Interface Clock gate +//------------------------------------------------ +`ifdef DBG_EN + `ifdef ASIC + omsp_clock_gate clock_gate_dbg_clk ( + .gclk (dbg_clk), + .clk (mclk), + .enable (dbg_en_s), + .scan_enable (scan_enable) + ); + `else + assign dbg_clk = dco_clk; + `endif +`else + assign dbg_clk = 1'b0; +`endif + + +//============================================================================= +// 7) RESET GENERATION +//============================================================================= +// +// Whenever the reset pin (reset_n) is deasserted, the internal resets of the +// openMSP430 will be released in the following order: +// 1- POR +// 2- DBG_RST (if the sdi interface is enabled, i.e. dbg_en=1) +// 3- PUC +// +// Note: releasing the DBG_RST before PUC is particularly important in order +// to allow the sdi interface to halt the cpu immediately after a PUC. +// + +// Generate synchronized POR to MCLK domain +//------------------------------------------ + +// Asynchronous reset source +assign por_a = !reset_n; +wire por_noscan; + +// Reset Synchronizer +omsp_sync_reset sync_reset_por ( + .rst_s (por_noscan), + .clk (nodiv_mclk), + .rst_a (por_a) +); + +// Scan Reset Mux +`ifdef ASIC +omsp_scan_mux scan_mux_por ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (por_noscan), + .data_out (por) +); +`else + assign por = por_noscan; +`endif + +// Generate synchronized reset for the SDI +//------------------------------------------ +`ifdef DBG_EN + +// Reset Generation +reg dbg_rst_noscan; +always @ (posedge mclk or posedge por) + if (por) dbg_rst_noscan <= 1'b1; + else dbg_rst_noscan <= dbg_rst_nxt; + + // Scan Reset Mux + `ifdef ASIC + omsp_scan_mux scan_mux_dbg_rst ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (dbg_rst_noscan), + .data_out (dbg_rst) + ); + `else + assign dbg_rst = dbg_rst_noscan; + `endif + +`else + wire dbg_rst_noscan = 1'b1; + assign dbg_rst = 1'b1; +`endif + + +// Generate main system reset (PUC_RST) +//-------------------------------------- +wire puc_noscan_n; +wire puc_a_scan; + +// Asynchronous PUC reset +wire puc_a = por | wdt_reset; + +// Synchronous PUC reset +wire puc_s = dbg_cpu_reset | // With the debug interface command + + (dbg_en_s & dbg_rst_noscan & ~puc_noscan_n); // Sequencing making sure PUC is released + // after DBG_RST if the debug interface is + // enabled at power-on-reset time +// Scan Reset Mux +`ifdef ASIC +omsp_scan_mux scan_mux_puc_rst_a ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (puc_a), + .data_out (puc_a_scan) +); +`else + assign puc_a_scan = puc_a; +`endif + +// Reset Synchronizer +// (required because of the asynchronous watchdog reset) +omsp_sync_cell sync_cell_puc ( + .data_out (puc_noscan_n), + .data_in (~puc_s), + .clk (mclk), + .rst (puc_a_scan) +); + +// Scan Reset Mux +`ifdef ASIC +omsp_scan_mux scan_mux_puc_rst ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (~puc_noscan_n), + .data_out (puc_rst) +); +`else + assign puc_rst = ~puc_noscan_n; +`endif + +// PUC pending set the serial debug interface +assign puc_pnd_set = ~puc_noscan_n; + + +endmodule // omsp_clock_module + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_clock_mux.v b/tests/openmsp430/rtl/omsp_clock_mux.v new file mode 100644 index 000000000..5f8406ad0 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_clock_mux.v @@ -0,0 +1,192 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_clock_mux.v +// +// *Module Description: +// Standard clock mux for the openMSP430 +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_clock_mux ( + +// OUTPUTs + clk_out, // Clock output + +// INPUTs + clk_in0, // Clock input 0 + clk_in1, // Clock input 1 + reset, // Reset + scan_mode, // Scan mode (clk_in0 is selected in scan mode) + select // Clock selection +); + +// OUTPUTs +//========= +output clk_out; // Clock output + +// INPUTs +//========= +input clk_in0; // Clock input 0 +input clk_in1; // Clock input 1 +input reset; // Reset +input scan_mode; // Scan mode (clk_in0 is selected in scan mode) +input select; // Clock selection + + +//===========================================================================================================================// +// 1) CLOCK MUX // +//===========================================================================================================================// +// // +// The following (glitch free) clock mux is implemented as following: // +// // +// // +// // +// // +// +-----. +--------+ +--------+ // +// select >>----+-------------O| \ | | | | +-----. // +// | | |---| D Q |---| D Q |--+-------| \ // +// | +-------O| / | | | | | | |O-+ // +// | | +-----' | | | | | +--O| / | // +// | | | /\ | | /\ | | | +-----' | // +// | | +--+--+--+ +--+--+--+ | | | // +// | | O | | | | // +// | | | | | | | +-----. // +// clk_in0 >>----------------------------------+------------+-----------+ +--| \ // +// | | | | |----<< clk_out // +// | | +---------------------------------------+ +--| / // +// | | | | +-----' // +// | +---------------------------------------------+ | // +// | | | | // +// | | +-----. +--------+ +--------+ | | // +// | +-O| \ | | | | | +-----. | // +// | | |---| D Q |---| D Q |--+-------| \ | // +// +--------------| / | | | | | |O-+ // +// +-----' | | | | +--O| / // +// | /\ | | /\ | | +-----' // +// +--+--+--+ +--+--+--+ | // +// O | | // +// | | | // +// clk_in1 >>----------------------------------+------------+-----------+ // +// // +// // +//===========================================================================================================================// + +//----------------------------------------------------------------------------- +// Wire declarations +//----------------------------------------------------------------------------- + +wire in0_select; +reg in0_select_s; +reg in0_select_ss; +wire in0_enable; + +wire in1_select; +reg in1_select_s; +reg in1_select_ss; +wire in1_enable; + +wire clk_in0_inv; +wire clk_in1_inv; +wire gated_clk_in0; +wire gated_clk_in1; + + +//----------------------------------------------------------------------------- +// CLK_IN0 Selection +//----------------------------------------------------------------------------- + +assign in0_select = ~select & ~in1_select_ss; + +always @ (posedge clk_in0_inv or posedge reset) + if (reset) in0_select_s <= 1'b1; + else in0_select_s <= in0_select; + +always @ (posedge clk_in0 or posedge reset) + if (reset) in0_select_ss <= 1'b1; + else in0_select_ss <= in0_select_s; + +assign in0_enable = in0_select_ss | scan_mode; + + +//----------------------------------------------------------------------------- +// CLK_IN1 Selection +//----------------------------------------------------------------------------- + +assign in1_select = select & ~in0_select_ss; + +always @ (posedge clk_in1_inv or posedge reset) + if (reset) in1_select_s <= 1'b0; + else in1_select_s <= in1_select; + +always @ (posedge clk_in1 or posedge reset) + if (reset) in1_select_ss <= 1'b0; + else in1_select_ss <= in1_select_s; + +assign in1_enable = in1_select_ss & ~scan_mode; + + +//----------------------------------------------------------------------------- +// Clock MUX +//----------------------------------------------------------------------------- +// +// IMPORTANT NOTE: +// Because the clock network is a critical part of the design, +// the following combinatorial logic should be replaced with +// direct instanciation of standard cells from target library. +// Don't forget the "dont_touch" attribute to make sure +// synthesis won't mess it up. +// + +// Replace with standard cell INVERTER +assign clk_in0_inv = ~clk_in0; +assign clk_in1_inv = ~clk_in1; + + +// Replace with standard cell NAND2 +assign gated_clk_in0 = ~(clk_in0_inv & in0_enable); +assign gated_clk_in1 = ~(clk_in1_inv & in1_enable); + + +// Replace with standard cell AND2 +assign clk_out = (gated_clk_in0 & gated_clk_in1); + + + +endmodule // omsp_clock_gate + + + diff --git a/tests/openmsp430/rtl/omsp_dbg.v b/tests/openmsp430/rtl/omsp_dbg.v new file mode 100644 index 000000000..97e9ede44 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_dbg.v @@ -0,0 +1,827 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_dbg.v +// +// *Module Description: +// Debug interface +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 149 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-07-19 22:21:12 +0200 (Thu, 19 Jul 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_dbg ( + +// OUTPUTs + dbg_freeze, // Freeze peripherals + dbg_halt_cmd, // Halt CPU command + dbg_mem_addr, // Debug address for rd/wr access + dbg_mem_dout, // Debug unit data output + dbg_mem_en, // Debug unit memory enable + dbg_mem_wr, // Debug unit memory write + dbg_reg_wr, // Debug unit CPU register write + dbg_cpu_reset, // Reset CPU from debug interface + dbg_uart_txd, // Debug interface: UART TXD + +// INPUTs + cpu_en_s, // Enable CPU code execution (synchronous) + cpu_id, // CPU ID + dbg_clk, // Debug unit clock + dbg_en_s, // Debug interface enable (synchronous) + dbg_halt_st, // Halt/Run status from CPU + dbg_mem_din, // Debug unit Memory data input + dbg_reg_din, // Debug unit CPU register data input + dbg_rst, // Debug unit reset + dbg_uart_rxd, // Debug interface: UART RXD (asynchronous) + decode_noirq, // Frontend decode instruction + eu_mab, // Execution-Unit Memory address bus + eu_mb_en, // Execution-Unit Memory bus enable + eu_mb_wr, // Execution-Unit Memory bus write transfer + eu_mdb_in, // Memory data bus input + eu_mdb_out, // Memory data bus output + exec_done, // Execution completed + fe_mb_en, // Frontend Memory bus enable + fe_mdb_in, // Frontend Memory data bus input + pc, // Program counter + puc_pnd_set // PUC pending set for the serial debug interface +); + +// OUTPUTs +//========= +output dbg_freeze; // Freeze peripherals +output dbg_halt_cmd; // Halt CPU command +output [15:0] dbg_mem_addr; // Debug address for rd/wr access +output [15:0] dbg_mem_dout; // Debug unit data output +output dbg_mem_en; // Debug unit memory enable +output [1:0] dbg_mem_wr; // Debug unit memory write +output dbg_reg_wr; // Debug unit CPU register write +output dbg_cpu_reset; // Reset CPU from debug interface +output dbg_uart_txd; // Debug interface: UART TXD + +// INPUTs +//========= +input cpu_en_s; // Enable CPU code execution (synchronous) +input [31:0] cpu_id; // CPU ID +input dbg_clk; // Debug unit clock +input dbg_en_s; // Debug interface enable (synchronous) +input dbg_halt_st; // Halt/Run status from CPU +input [15:0] dbg_mem_din; // Debug unit Memory data input +input [15:0] dbg_reg_din; // Debug unit CPU register data input +input dbg_rst; // Debug unit reset +input dbg_uart_rxd; // Debug interface: UART RXD (asynchronous) +input decode_noirq; // Frontend decode instruction +input [15:0] eu_mab; // Execution-Unit Memory address bus +input eu_mb_en; // Execution-Unit Memory bus enable +input [1:0] eu_mb_wr; // Execution-Unit Memory bus write transfer +input [15:0] eu_mdb_in; // Memory data bus input +input [15:0] eu_mdb_out; // Memory data bus output +input exec_done; // Execution completed +input fe_mb_en; // Frontend Memory bus enable +input [15:0] fe_mdb_in; // Frontend Memory data bus input +input [15:0] pc; // Program counter +input puc_pnd_set; // PUC pending set for the serial debug interface + + +//============================================================================= +// 1) WIRE & PARAMETER DECLARATION +//============================================================================= + +// Diverse wires and registers +wire [5:0] dbg_addr; +wire [15:0] dbg_din; +wire dbg_wr; +reg mem_burst; +wire dbg_reg_rd; +wire dbg_mem_rd; +reg dbg_mem_rd_dly; +wire dbg_swbrk; +wire dbg_rd; +reg dbg_rd_rdy; +wire mem_burst_rd; +wire mem_burst_wr; +wire brk0_halt; +wire brk0_pnd; +wire [15:0] brk0_dout; +wire brk1_halt; +wire brk1_pnd; +wire [15:0] brk1_dout; +wire brk2_halt; +wire brk2_pnd; +wire [15:0] brk2_dout; +wire brk3_halt; +wire brk3_pnd; +wire [15:0] brk3_dout; + +// Number of registers +parameter NR_REG = 24; + +// Register addresses +parameter CPU_ID_LO = 6'h00; +parameter CPU_ID_HI = 6'h01; +parameter CPU_CTL = 6'h02; +parameter CPU_STAT = 6'h03; +parameter MEM_CTL = 6'h04; +parameter MEM_ADDR = 6'h05; +parameter MEM_DATA = 6'h06; +parameter MEM_CNT = 6'h07; +`ifdef DBG_HWBRK_0 +parameter BRK0_CTL = 6'h08; +parameter BRK0_STAT = 6'h09; +parameter BRK0_ADDR0 = 6'h0A; +parameter BRK0_ADDR1 = 6'h0B; +`endif +`ifdef DBG_HWBRK_1 +parameter BRK1_CTL = 6'h0C; +parameter BRK1_STAT = 6'h0D; +parameter BRK1_ADDR0 = 6'h0E; +parameter BRK1_ADDR1 = 6'h0F; +`endif +`ifdef DBG_HWBRK_2 +parameter BRK2_CTL = 6'h10; +parameter BRK2_STAT = 6'h11; +parameter BRK2_ADDR0 = 6'h12; +parameter BRK2_ADDR1 = 6'h13; +`endif +`ifdef DBG_HWBRK_3 +parameter BRK3_CTL = 6'h14; +parameter BRK3_STAT = 6'h15; +parameter BRK3_ADDR0 = 6'h16; +parameter BRK3_ADDR1 = 6'h17; +`endif + +// Register one-hot decoder +parameter BASE_D = {{NR_REG-1{1'b0}}, 1'b1}; +parameter CPU_ID_LO_D = (BASE_D << CPU_ID_LO); +parameter CPU_ID_HI_D = (BASE_D << CPU_ID_HI); +parameter CPU_CTL_D = (BASE_D << CPU_CTL); +parameter CPU_STAT_D = (BASE_D << CPU_STAT); +parameter MEM_CTL_D = (BASE_D << MEM_CTL); +parameter MEM_ADDR_D = (BASE_D << MEM_ADDR); +parameter MEM_DATA_D = (BASE_D << MEM_DATA); +parameter MEM_CNT_D = (BASE_D << MEM_CNT); +`ifdef DBG_HWBRK_0 +parameter BRK0_CTL_D = (BASE_D << BRK0_CTL); +parameter BRK0_STAT_D = (BASE_D << BRK0_STAT); +parameter BRK0_ADDR0_D = (BASE_D << BRK0_ADDR0); +parameter BRK0_ADDR1_D = (BASE_D << BRK0_ADDR1); +`endif +`ifdef DBG_HWBRK_1 +parameter BRK1_CTL_D = (BASE_D << BRK1_CTL); +parameter BRK1_STAT_D = (BASE_D << BRK1_STAT); +parameter BRK1_ADDR0_D = (BASE_D << BRK1_ADDR0); +parameter BRK1_ADDR1_D = (BASE_D << BRK1_ADDR1); +`endif +`ifdef DBG_HWBRK_2 +parameter BRK2_CTL_D = (BASE_D << BRK2_CTL); +parameter BRK2_STAT_D = (BASE_D << BRK2_STAT); +parameter BRK2_ADDR0_D = (BASE_D << BRK2_ADDR0); +parameter BRK2_ADDR1_D = (BASE_D << BRK2_ADDR1); +`endif +`ifdef DBG_HWBRK_3 +parameter BRK3_CTL_D = (BASE_D << BRK3_CTL); +parameter BRK3_STAT_D = (BASE_D << BRK3_STAT); +parameter BRK3_ADDR0_D = (BASE_D << BRK3_ADDR0); +parameter BRK3_ADDR1_D = (BASE_D << BRK3_ADDR1); +`endif + + +//============================================================================ +// 2) REGISTER DECODER +//============================================================================ + +// Select Data register during a burst +wire [5:0] dbg_addr_in = mem_burst ? MEM_DATA : dbg_addr; + +// Register address decode +reg [NR_REG-1:0] reg_dec; +always @(dbg_addr_in) + case (dbg_addr_in) + CPU_ID_LO : reg_dec = CPU_ID_LO_D; + CPU_ID_HI : reg_dec = CPU_ID_HI_D; + CPU_CTL : reg_dec = CPU_CTL_D; + CPU_STAT : reg_dec = CPU_STAT_D; + MEM_CTL : reg_dec = MEM_CTL_D; + MEM_ADDR : reg_dec = MEM_ADDR_D; + MEM_DATA : reg_dec = MEM_DATA_D; + MEM_CNT : reg_dec = MEM_CNT_D; +`ifdef DBG_HWBRK_0 + BRK0_CTL : reg_dec = BRK0_CTL_D; + BRK0_STAT : reg_dec = BRK0_STAT_D; + BRK0_ADDR0: reg_dec = BRK0_ADDR0_D; + BRK0_ADDR1: reg_dec = BRK0_ADDR1_D; +`endif +`ifdef DBG_HWBRK_1 + BRK1_CTL : reg_dec = BRK1_CTL_D; + BRK1_STAT : reg_dec = BRK1_STAT_D; + BRK1_ADDR0: reg_dec = BRK1_ADDR0_D; + BRK1_ADDR1: reg_dec = BRK1_ADDR1_D; +`endif +`ifdef DBG_HWBRK_2 + BRK2_CTL : reg_dec = BRK2_CTL_D; + BRK2_STAT : reg_dec = BRK2_STAT_D; + BRK2_ADDR0: reg_dec = BRK2_ADDR0_D; + BRK2_ADDR1: reg_dec = BRK2_ADDR1_D; +`endif +`ifdef DBG_HWBRK_3 + BRK3_CTL : reg_dec = BRK3_CTL_D; + BRK3_STAT : reg_dec = BRK3_STAT_D; + BRK3_ADDR0: reg_dec = BRK3_ADDR0_D; + BRK3_ADDR1: reg_dec = BRK3_ADDR1_D; +`endif + // pragma coverage off + default: reg_dec = {NR_REG{1'b0}}; + // pragma coverage on + endcase + +// Read/Write probes +wire reg_write = dbg_wr; +wire reg_read = 1'b1; + +// Read/Write vectors +wire [NR_REG-1:0] reg_wr = reg_dec & {NR_REG{reg_write}}; +wire [NR_REG-1:0] reg_rd = reg_dec & {NR_REG{reg_read}}; + + +//============================================================================= +// 3) REGISTER: CORE INTERFACE +//============================================================================= + +// CPU_ID Register +//----------------- +// ------------------------------------------------------------------- +// CPU_ID_LO: | 15 14 13 12 11 10 9 | 8 7 6 5 4 | 3 | 2 1 0 | +// |----------------------------+-----------------+------+-------------| +// | PER_SPACE | USER_VERSION | ASIC | CPU_VERSION | +// -------------------------------------------------------------------- +// CPU_ID_HI: | 15 14 13 12 11 10 | 9 8 7 6 5 4 3 2 1 | 0 | +// |----------------------------+-------------------------------+------| +// | PMEM_SIZE | DMEM_SIZE | MPY | +// ------------------------------------------------------------------- + +// This register is assigned in the SFR module + + +// CPU_CTL Register +//----------------------------------------------------------------------------- +// 7 6 5 4 3 2 1 0 +// Reserved CPU_RST RST_BRK_EN FRZ_BRK_EN SW_BRK_EN ISTEP RUN HALT +//----------------------------------------------------------------------------- +reg [6:3] cpu_ctl; + +wire cpu_ctl_wr = reg_wr[CPU_CTL]; + +always @ (posedge dbg_clk or posedge dbg_rst) +`ifdef DBG_RST_BRK_EN + if (dbg_rst) cpu_ctl <= 4'h6; +`else + if (dbg_rst) cpu_ctl <= 4'h2; +`endif + else if (cpu_ctl_wr) cpu_ctl <= dbg_din[6:3]; + +wire [7:0] cpu_ctl_full = {1'b0, cpu_ctl, 3'b000}; + +wire halt_cpu = cpu_ctl_wr & dbg_din[`HALT] & ~dbg_halt_st; +wire run_cpu = cpu_ctl_wr & dbg_din[`RUN] & dbg_halt_st; +wire istep = cpu_ctl_wr & dbg_din[`ISTEP] & dbg_halt_st; + + +// CPU_STAT Register +//------------------------------------------------------------------------------------ +// 7 6 5 4 3 2 1 0 +// HWBRK3_PND HWBRK2_PND HWBRK1_PND HWBRK0_PND SWBRK_PND PUC_PND Res. HALT_RUN +//------------------------------------------------------------------------------------ +reg [3:2] cpu_stat; + +wire cpu_stat_wr = reg_wr[CPU_STAT]; +wire [3:2] cpu_stat_set = {dbg_swbrk, puc_pnd_set}; +wire [3:2] cpu_stat_clr = ~dbg_din[3:2]; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) cpu_stat <= 2'b00; + else if (cpu_stat_wr) cpu_stat <= ((cpu_stat & cpu_stat_clr) | cpu_stat_set); + else cpu_stat <= (cpu_stat | cpu_stat_set); + +wire [7:0] cpu_stat_full = {brk3_pnd, brk2_pnd, brk1_pnd, brk0_pnd, + cpu_stat, 1'b0, dbg_halt_st}; + + +//============================================================================= +// 4) REGISTER: MEMORY INTERFACE +//============================================================================= + +// MEM_CTL Register +//----------------------------------------------------------------------------- +// 7 6 5 4 3 2 1 0 +// Reserved B/W MEM/REG RD/WR START +// +// START : - 0 : Do nothing. +// - 1 : Initiate memory transfer. +// +// RD/WR : - 0 : Read access. +// - 1 : Write access. +// +// MEM/REG: - 0 : Memory access. +// - 1 : CPU Register access. +// +// B/W : - 0 : 16 bit access. +// - 1 : 8 bit access (not valid for CPU Registers). +// +//----------------------------------------------------------------------------- +reg [3:1] mem_ctl; + +wire mem_ctl_wr = reg_wr[MEM_CTL]; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_ctl <= 3'h0; + else if (mem_ctl_wr) mem_ctl <= dbg_din[3:1]; + +wire [7:0] mem_ctl_full = {4'b0000, mem_ctl, 1'b0}; + +reg mem_start; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_start <= 1'b0; + else mem_start <= mem_ctl_wr & dbg_din[0]; + +wire mem_bw = mem_ctl[3]; + +// MEM_DATA Register +//------------------ +reg [15:0] mem_data; +reg [15:0] mem_addr; +wire mem_access; + +wire mem_data_wr = reg_wr[MEM_DATA]; + +wire [15:0] dbg_mem_din_bw = ~mem_bw ? dbg_mem_din : + mem_addr[0] ? {8'h00, dbg_mem_din[15:8]} : + {8'h00, dbg_mem_din[7:0]}; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_data <= 16'h0000; + else if (mem_data_wr) mem_data <= dbg_din; + else if (dbg_reg_rd) mem_data <= dbg_reg_din; + else if (dbg_mem_rd_dly) mem_data <= dbg_mem_din_bw; + + +// MEM_ADDR Register +//------------------ +reg [15:0] mem_cnt; + +wire mem_addr_wr = reg_wr[MEM_ADDR]; +wire dbg_mem_acc = (|dbg_mem_wr | (dbg_rd_rdy & ~mem_ctl[2])); +wire dbg_reg_acc = ( dbg_reg_wr | (dbg_rd_rdy & mem_ctl[2])); + +wire [15:0] mem_addr_inc = (mem_cnt==16'h0000) ? 16'h0000 : + (dbg_mem_acc & ~mem_bw) ? 16'h0002 : + (dbg_mem_acc | dbg_reg_acc) ? 16'h0001 : 16'h0000; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_addr <= 16'h0000; + else if (mem_addr_wr) mem_addr <= dbg_din; + else mem_addr <= mem_addr + mem_addr_inc; + +// MEM_CNT Register +//------------------ + +wire mem_cnt_wr = reg_wr[MEM_CNT]; + +wire [15:0] mem_cnt_dec = (mem_cnt==16'h0000) ? 16'h0000 : + (mem_burst & (dbg_mem_acc | dbg_reg_acc)) ? 16'hffff : 16'h0000; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_cnt <= 16'h0000; + else if (mem_cnt_wr) mem_cnt <= dbg_din; + else mem_cnt <= mem_cnt + mem_cnt_dec; + + +//============================================================================= +// 5) BREAKPOINTS / WATCHPOINTS +//============================================================================= + +`ifdef DBG_HWBRK_0 +// Hardware Breakpoint/Watchpoint Register read select +wire [3:0] brk0_reg_rd = {reg_rd[BRK0_ADDR1], + reg_rd[BRK0_ADDR0], + reg_rd[BRK0_STAT], + reg_rd[BRK0_CTL]}; + +// Hardware Breakpoint/Watchpoint Register write select +wire [3:0] brk0_reg_wr = {reg_wr[BRK0_ADDR1], + reg_wr[BRK0_ADDR0], + reg_wr[BRK0_STAT], + reg_wr[BRK0_CTL]}; + +omsp_dbg_hwbrk dbg_hwbr_0 ( + +// OUTPUTs + .brk_halt (brk0_halt), // Hardware breakpoint command + .brk_pnd (brk0_pnd), // Hardware break/watch-point pending + .brk_dout (brk0_dout), // Hardware break/watch-point register data input + +// INPUTs + .brk_reg_rd (brk0_reg_rd), // Hardware break/watch-point register read select + .brk_reg_wr (brk0_reg_wr), // Hardware break/watch-point register write select + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_din (dbg_din), // Debug register data input + .dbg_rst (dbg_rst), // Debug unit reset + .eu_mab (eu_mab), // Execution-Unit Memory address bus + .eu_mb_en (eu_mb_en), // Execution-Unit Memory bus enable + .eu_mb_wr (eu_mb_wr), // Execution-Unit Memory bus write transfer + .eu_mdb_in (eu_mdb_in), // Memory data bus input + .eu_mdb_out (eu_mdb_out), // Memory data bus output + .exec_done (exec_done), // Execution completed + .fe_mb_en (fe_mb_en), // Frontend Memory bus enable + .pc (pc) // Program counter +); + +`else +assign brk0_halt = 1'b0; +assign brk0_pnd = 1'b0; +assign brk0_dout = 16'h0000; +`endif + +`ifdef DBG_HWBRK_1 +// Hardware Breakpoint/Watchpoint Register read select +wire [3:0] brk1_reg_rd = {reg_rd[BRK1_ADDR1], + reg_rd[BRK1_ADDR0], + reg_rd[BRK1_STAT], + reg_rd[BRK1_CTL]}; + +// Hardware Breakpoint/Watchpoint Register write select +wire [3:0] brk1_reg_wr = {reg_wr[BRK1_ADDR1], + reg_wr[BRK1_ADDR0], + reg_wr[BRK1_STAT], + reg_wr[BRK1_CTL]}; + +omsp_dbg_hwbrk dbg_hwbr_1 ( + +// OUTPUTs + .brk_halt (brk1_halt), // Hardware breakpoint command + .brk_pnd (brk1_pnd), // Hardware break/watch-point pending + .brk_dout (brk1_dout), // Hardware break/watch-point register data input + +// INPUTs + .brk_reg_rd (brk1_reg_rd), // Hardware break/watch-point register read select + .brk_reg_wr (brk1_reg_wr), // Hardware break/watch-point register write select + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_din (dbg_din), // Debug register data input + .dbg_rst (dbg_rst), // Debug unit reset + .eu_mab (eu_mab), // Execution-Unit Memory address bus + .eu_mb_en (eu_mb_en), // Execution-Unit Memory bus enable + .eu_mb_wr (eu_mb_wr), // Execution-Unit Memory bus write transfer + .eu_mdb_in (eu_mdb_in), // Memory data bus input + .eu_mdb_out (eu_mdb_out), // Memory data bus output + .exec_done (exec_done), // Execution completed + .fe_mb_en (fe_mb_en), // Frontend Memory bus enable + .pc (pc) // Program counter +); + +`else +assign brk1_halt = 1'b0; +assign brk1_pnd = 1'b0; +assign brk1_dout = 16'h0000; +`endif + + `ifdef DBG_HWBRK_2 +// Hardware Breakpoint/Watchpoint Register read select +wire [3:0] brk2_reg_rd = {reg_rd[BRK2_ADDR1], + reg_rd[BRK2_ADDR0], + reg_rd[BRK2_STAT], + reg_rd[BRK2_CTL]}; + +// Hardware Breakpoint/Watchpoint Register write select +wire [3:0] brk2_reg_wr = {reg_wr[BRK2_ADDR1], + reg_wr[BRK2_ADDR0], + reg_wr[BRK2_STAT], + reg_wr[BRK2_CTL]}; + +omsp_dbg_hwbrk dbg_hwbr_2 ( + +// OUTPUTs + .brk_halt (brk2_halt), // Hardware breakpoint command + .brk_pnd (brk2_pnd), // Hardware break/watch-point pending + .brk_dout (brk2_dout), // Hardware break/watch-point register data input + +// INPUTs + .brk_reg_rd (brk2_reg_rd), // Hardware break/watch-point register read select + .brk_reg_wr (brk2_reg_wr), // Hardware break/watch-point register write select + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_din (dbg_din), // Debug register data input + .dbg_rst (dbg_rst), // Debug unit reset + .eu_mab (eu_mab), // Execution-Unit Memory address bus + .eu_mb_en (eu_mb_en), // Execution-Unit Memory bus enable + .eu_mb_wr (eu_mb_wr), // Execution-Unit Memory bus write transfer + .eu_mdb_in (eu_mdb_in), // Memory data bus input + .eu_mdb_out (eu_mdb_out), // Memory data bus output + .exec_done (exec_done), // Execution completed + .fe_mb_en (fe_mb_en), // Frontend Memory bus enable + .pc (pc) // Program counter +); + +`else +assign brk2_halt = 1'b0; +assign brk2_pnd = 1'b0; +assign brk2_dout = 16'h0000; +`endif + +`ifdef DBG_HWBRK_3 +// Hardware Breakpoint/Watchpoint Register read select +wire [3:0] brk3_reg_rd = {reg_rd[BRK3_ADDR1], + reg_rd[BRK3_ADDR0], + reg_rd[BRK3_STAT], + reg_rd[BRK3_CTL]}; + +// Hardware Breakpoint/Watchpoint Register write select +wire [3:0] brk3_reg_wr = {reg_wr[BRK3_ADDR1], + reg_wr[BRK3_ADDR0], + reg_wr[BRK3_STAT], + reg_wr[BRK3_CTL]}; + +omsp_dbg_hwbrk dbg_hwbr_3 ( + +// OUTPUTs + .brk_halt (brk3_halt), // Hardware breakpoint command + .brk_pnd (brk3_pnd), // Hardware break/watch-point pending + .brk_dout (brk3_dout), // Hardware break/watch-point register data input + +// INPUTs + .brk_reg_rd (brk3_reg_rd), // Hardware break/watch-point register read select + .brk_reg_wr (brk3_reg_wr), // Hardware break/watch-point register write select + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_din (dbg_din), // Debug register data input + .dbg_rst (dbg_rst), // Debug unit reset + .eu_mab (eu_mab), // Execution-Unit Memory address bus + .eu_mb_en (eu_mb_en), // Execution-Unit Memory bus enable + .eu_mb_wr (eu_mb_wr), // Execution-Unit Memory bus write transfer + .eu_mdb_in (eu_mdb_in), // Memory data bus input + .eu_mdb_out (eu_mdb_out), // Memory data bus output + .exec_done (exec_done), // Execution completed + .fe_mb_en (fe_mb_en), // Frontend Memory bus enable + .pc (pc) // Program counter +); + +`else +assign brk3_halt = 1'b0; +assign brk3_pnd = 1'b0; +assign brk3_dout = 16'h0000; +`endif + + +//============================================================================ +// 6) DATA OUTPUT GENERATION +//============================================================================ + +wire [15:0] cpu_id_lo_rd = cpu_id[15:0] & {16{reg_rd[CPU_ID_LO]}}; +wire [15:0] cpu_id_hi_rd = cpu_id[31:16] & {16{reg_rd[CPU_ID_HI]}}; +wire [15:0] cpu_ctl_rd = {8'h00, cpu_ctl_full} & {16{reg_rd[CPU_CTL]}}; +wire [15:0] cpu_stat_rd = {8'h00, cpu_stat_full} & {16{reg_rd[CPU_STAT]}}; +wire [15:0] mem_ctl_rd = {8'h00, mem_ctl_full} & {16{reg_rd[MEM_CTL]}}; +wire [15:0] mem_data_rd = mem_data & {16{reg_rd[MEM_DATA]}}; +wire [15:0] mem_addr_rd = mem_addr & {16{reg_rd[MEM_ADDR]}}; +wire [15:0] mem_cnt_rd = mem_cnt & {16{reg_rd[MEM_CNT]}}; + +wire [15:0] dbg_dout = cpu_id_lo_rd | + cpu_id_hi_rd | + cpu_ctl_rd | + cpu_stat_rd | + mem_ctl_rd | + mem_data_rd | + mem_addr_rd | + mem_cnt_rd | + brk0_dout | + brk1_dout | + brk2_dout | + brk3_dout; + +// Tell UART/JTAG interface that the data is ready to be read +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) dbg_rd_rdy <= 1'b0; + else if (mem_burst | mem_burst_rd) dbg_rd_rdy <= (dbg_reg_rd | dbg_mem_rd_dly); + else dbg_rd_rdy <= dbg_rd; + + +//============================================================================ +// 7) CPU CONTROL +//============================================================================ + +// Reset CPU +//-------------------------- +wire dbg_cpu_reset = cpu_ctl[`CPU_RST]; + + +// Break after reset +//-------------------------- +wire halt_rst = cpu_ctl[`RST_BRK_EN] & dbg_en_s & puc_pnd_set; + + +// Freeze peripherals +//-------------------------- +wire dbg_freeze = dbg_halt_st & (cpu_ctl[`FRZ_BRK_EN] | ~cpu_en_s); + + +// Software break +//-------------------------- +assign dbg_swbrk = (fe_mdb_in==`DBG_SWBRK_OP) & decode_noirq & cpu_ctl[`SW_BRK_EN]; + + +// Single step +//-------------------------- +reg [1:0] inc_step; +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) inc_step <= 2'b00; + else if (istep) inc_step <= 2'b11; + else inc_step <= {inc_step[0], 1'b0}; + + +// Run / Halt +//-------------------------- +reg halt_flag; + +wire mem_halt_cpu; +wire mem_run_cpu; + +wire halt_flag_clr = run_cpu | mem_run_cpu; +wire halt_flag_set = halt_cpu | halt_rst | dbg_swbrk | mem_halt_cpu | + brk0_halt | brk1_halt | brk2_halt | brk3_halt; + +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) halt_flag <= 1'b0; + else if (halt_flag_clr) halt_flag <= 1'b0; + else if (halt_flag_set) halt_flag <= 1'b1; + +wire dbg_halt_cmd = (halt_flag | halt_flag_set) & ~inc_step[1]; + + +//============================================================================ +// 8) MEMORY CONTROL +//============================================================================ + +// Control Memory bursts +//------------------------------ + +wire mem_burst_start = (mem_start & |mem_cnt); +wire mem_burst_end = ((dbg_wr | dbg_rd_rdy) & ~|mem_cnt); + +// Detect when burst is on going +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_burst <= 1'b0; + else if (mem_burst_start) mem_burst <= 1'b1; + else if (mem_burst_end) mem_burst <= 1'b0; + +// Control signals for UART/JTAG interface +assign mem_burst_rd = (mem_burst_start & ~mem_ctl[1]); +assign mem_burst_wr = (mem_burst_start & mem_ctl[1]); + +// Trigger CPU Register or memory access during a burst +reg mem_startb; +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_startb <= 1'b0; + else mem_startb <= (mem_burst & (dbg_wr | dbg_rd)) | mem_burst_rd; + +// Combine single and burst memory start of sequence +wire mem_seq_start = ((mem_start & ~|mem_cnt) | mem_startb); + + +// Memory access state machine +//------------------------------ +reg [1:0] mem_state; +reg [1:0] mem_state_nxt; + +// State machine definition +parameter M_IDLE = 2'h0; +parameter M_SET_BRK = 2'h1; +parameter M_ACCESS_BRK = 2'h2; +parameter M_ACCESS = 2'h3; + +// State transition +always @(mem_state or mem_seq_start or dbg_halt_st) + case (mem_state) + M_IDLE : mem_state_nxt = ~mem_seq_start ? M_IDLE : + dbg_halt_st ? M_ACCESS : M_SET_BRK; + M_SET_BRK : mem_state_nxt = dbg_halt_st ? M_ACCESS_BRK : M_SET_BRK; + M_ACCESS_BRK : mem_state_nxt = M_IDLE; + M_ACCESS : mem_state_nxt = M_IDLE; + // pragma coverage off + default : mem_state_nxt = M_IDLE; + // pragma coverage on + endcase + +// State machine +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) mem_state <= M_IDLE; + else mem_state <= mem_state_nxt; + +// Utility signals +assign mem_halt_cpu = (mem_state==M_IDLE) & (mem_state_nxt==M_SET_BRK); +assign mem_run_cpu = (mem_state==M_ACCESS_BRK) & (mem_state_nxt==M_IDLE); +assign mem_access = (mem_state==M_ACCESS) | (mem_state==M_ACCESS_BRK); + + +// Interface to CPU Registers and Memory bacbkone +//------------------------------------------------ +assign dbg_mem_addr = mem_addr; +assign dbg_mem_dout = ~mem_bw ? mem_data : + mem_addr[0] ? {mem_data[7:0], 8'h00} : + {8'h00, mem_data[7:0]}; + +assign dbg_reg_wr = mem_access & mem_ctl[1] & mem_ctl[2]; +assign dbg_reg_rd = mem_access & ~mem_ctl[1] & mem_ctl[2]; + +assign dbg_mem_en = mem_access & ~mem_ctl[2]; +assign dbg_mem_rd = dbg_mem_en & ~mem_ctl[1]; + +wire [1:0] dbg_mem_wr_msk = ~mem_bw ? 2'b11 : + mem_addr[0] ? 2'b10 : 2'b01; +assign dbg_mem_wr = {2{dbg_mem_en & mem_ctl[1]}} & dbg_mem_wr_msk; + + +// It takes one additional cycle to read from Memory as from registers +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) dbg_mem_rd_dly <= 1'b0; + else dbg_mem_rd_dly <= dbg_mem_rd; + + +//============================================================================= +// 9) UART COMMUNICATION +//============================================================================= +`ifdef DBG_UART +omsp_dbg_uart dbg_uart_0 ( + +// OUTPUTs + .dbg_addr (dbg_addr), // Debug register address + .dbg_din (dbg_din), // Debug register data input + .dbg_rd (dbg_rd), // Debug register data read + .dbg_uart_txd (dbg_uart_txd), // Debug interface: UART TXD + .dbg_wr (dbg_wr), // Debug register data write + +// INPUTs + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_dout (dbg_dout), // Debug register data output + .dbg_rd_rdy (dbg_rd_rdy), // Debug register data is ready for read + .dbg_rst (dbg_rst), // Debug unit reset + .dbg_uart_rxd (dbg_uart_rxd), // Debug interface: UART RXD + .mem_burst (mem_burst), // Burst on going + .mem_burst_end(mem_burst_end), // End TX/RX burst + .mem_burst_rd (mem_burst_rd), // Start TX burst + .mem_burst_wr (mem_burst_wr), // Start RX burst + .mem_bw (mem_bw) // Burst byte width +); + +`else +assign dbg_addr = 6'h00; +assign dbg_din = 16'h0000; +assign dbg_rd = 1'b0; +assign dbg_uart_txd = 1'b0; +assign dbg_wr = 1'b0; +`endif + + +//============================================================================= +// 10) JTAG COMMUNICATION +//============================================================================= +`ifdef DBG_JTAG +JTAG INTERFACE IS NOT SUPPORTED YET +`else +`endif + +endmodule // dbg + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_dbg_hwbrk.v b/tests/openmsp430/rtl/omsp_dbg_hwbrk.v new file mode 100644 index 000000000..6f639aee9 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_dbg_hwbrk.v @@ -0,0 +1,282 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_dbg_hwbrk.v +// +// *Module Description: +// Hardware Breakpoint / Watchpoint module +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 117 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-06-23 21:30:51 +0200 (Thu, 23 Jun 2011) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_dbg_hwbrk ( + +// OUTPUTs + brk_halt, // Hardware breakpoint command + brk_pnd, // Hardware break/watch-point pending + brk_dout, // Hardware break/watch-point register data input + +// INPUTs + brk_reg_rd, // Hardware break/watch-point register read select + brk_reg_wr, // Hardware break/watch-point register write select + dbg_clk, // Debug unit clock + dbg_din, // Debug register data input + dbg_rst, // Debug unit reset + eu_mab, // Execution-Unit Memory address bus + eu_mb_en, // Execution-Unit Memory bus enable + eu_mb_wr, // Execution-Unit Memory bus write transfer + eu_mdb_in, // Memory data bus input + eu_mdb_out, // Memory data bus output + exec_done, // Execution completed + fe_mb_en, // Frontend Memory bus enable + pc // Program counter +); + +// OUTPUTs +//========= +output brk_halt; // Hardware breakpoint command +output brk_pnd; // Hardware break/watch-point pending +output [15:0] brk_dout; // Hardware break/watch-point register data input + +// INPUTs +//========= +input [3:0] brk_reg_rd; // Hardware break/watch-point register read select +input [3:0] brk_reg_wr; // Hardware break/watch-point register write select +input dbg_clk; // Debug unit clock +input [15:0] dbg_din; // Debug register data input +input dbg_rst; // Debug unit reset +input [15:0] eu_mab; // Execution-Unit Memory address bus +input eu_mb_en; // Execution-Unit Memory bus enable +input [1:0] eu_mb_wr; // Execution-Unit Memory bus write transfer +input [15:0] eu_mdb_in; // Memory data bus input +input [15:0] eu_mdb_out; // Memory data bus output +input exec_done; // Execution completed +input fe_mb_en; // Frontend Memory bus enable +input [15:0] pc; // Program counter + + +//============================================================================= +// 1) WIRE & PARAMETER DECLARATION +//============================================================================= + +wire range_wr_set; +wire range_rd_set; +wire addr1_wr_set; +wire addr1_rd_set; +wire addr0_wr_set; +wire addr0_rd_set; + + +parameter BRK_CTL = 0, + BRK_STAT = 1, + BRK_ADDR0 = 2, + BRK_ADDR1 = 3; + + +//============================================================================= +// 2) CONFIGURATION REGISTERS +//============================================================================= + +// BRK_CTL Register +//----------------------------------------------------------------------------- +// 7 6 5 4 3 2 1 0 +// Reserved RANGE_MODE INST_EN BREAK_EN ACCESS_MODE +// +// ACCESS_MODE: - 00 : Disabled +// - 01 : Detect read access +// - 10 : Detect write access +// - 11 : Detect read/write access +// NOTE: '10' & '11' modes are not supported on the instruction flow +// +// BREAK_EN: - 0 : Watchmode enable +// - 1 : Break enable +// +// INST_EN: - 0 : Checks are done on the execution unit (data flow) +// - 1 : Checks are done on the frontend (instruction flow) +// +// RANGE_MODE: - 0 : Address match on BRK_ADDR0 or BRK_ADDR1 +// - 1 : Address match on BRK_ADDR0->BRK_ADDR1 range +// +//----------------------------------------------------------------------------- +reg [4:0] brk_ctl; + +wire brk_ctl_wr = brk_reg_wr[BRK_CTL]; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) brk_ctl <= 5'h00; + else if (brk_ctl_wr) brk_ctl <= {`HWBRK_RANGE & dbg_din[4], dbg_din[3:0]}; + +wire [7:0] brk_ctl_full = {3'b000, brk_ctl}; + + +// BRK_STAT Register +//----------------------------------------------------------------------------- +// 7 6 5 4 3 2 1 0 +// Reserved RANGE_WR RANGE_RD ADDR1_WR ADDR1_RD ADDR0_WR ADDR0_RD +//----------------------------------------------------------------------------- +reg [5:0] brk_stat; + +wire brk_stat_wr = brk_reg_wr[BRK_STAT]; +wire [5:0] brk_stat_set = {range_wr_set & `HWBRK_RANGE, + range_rd_set & `HWBRK_RANGE, + addr1_wr_set, addr1_rd_set, + addr0_wr_set, addr0_rd_set}; +wire [5:0] brk_stat_clr = ~dbg_din[5:0]; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) brk_stat <= 6'h00; + else if (brk_stat_wr) brk_stat <= ((brk_stat & brk_stat_clr) | brk_stat_set); + else brk_stat <= (brk_stat | brk_stat_set); + +wire [7:0] brk_stat_full = {2'b00, brk_stat}; +wire brk_pnd = |brk_stat; + + +// BRK_ADDR0 Register +//----------------------------------------------------------------------------- +reg [15:0] brk_addr0; + +wire brk_addr0_wr = brk_reg_wr[BRK_ADDR0]; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) brk_addr0 <= 16'h0000; + else if (brk_addr0_wr) brk_addr0 <= dbg_din; + + +// BRK_ADDR1/DATA0 Register +//----------------------------------------------------------------------------- +reg [15:0] brk_addr1; + +wire brk_addr1_wr = brk_reg_wr[BRK_ADDR1]; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) brk_addr1 <= 16'h0000; + else if (brk_addr1_wr) brk_addr1 <= dbg_din; + + +//============================================================================ +// 3) DATA OUTPUT GENERATION +//============================================================================ + +wire [15:0] brk_ctl_rd = {8'h00, brk_ctl_full} & {16{brk_reg_rd[BRK_CTL]}}; +wire [15:0] brk_stat_rd = {8'h00, brk_stat_full} & {16{brk_reg_rd[BRK_STAT]}}; +wire [15:0] brk_addr0_rd = brk_addr0 & {16{brk_reg_rd[BRK_ADDR0]}}; +wire [15:0] brk_addr1_rd = brk_addr1 & {16{brk_reg_rd[BRK_ADDR1]}}; + +wire [15:0] brk_dout = brk_ctl_rd | + brk_stat_rd | + brk_addr0_rd | + brk_addr1_rd; + + +//============================================================================ +// 4) BREAKPOINT / WATCHPOINT GENERATION +//============================================================================ + +// Comparators +//--------------------------- +// Note: here the comparison logic is instanciated several times in order +// to improve the timings, at the cost of a bit more area. + +wire equ_d_addr0 = eu_mb_en & (eu_mab==brk_addr0) & ~brk_ctl[`BRK_RANGE]; +wire equ_d_addr1 = eu_mb_en & (eu_mab==brk_addr1) & ~brk_ctl[`BRK_RANGE]; +wire equ_d_range = eu_mb_en & ((eu_mab>=brk_addr0) & (eu_mab<=brk_addr1)) & + brk_ctl[`BRK_RANGE] & `HWBRK_RANGE; + +reg fe_mb_en_buf; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) fe_mb_en_buf <= 1'b0; + else fe_mb_en_buf <= fe_mb_en; + +wire equ_i_addr0 = fe_mb_en_buf & (pc==brk_addr0) & ~brk_ctl[`BRK_RANGE]; +wire equ_i_addr1 = fe_mb_en_buf & (pc==brk_addr1) & ~brk_ctl[`BRK_RANGE]; +wire equ_i_range = fe_mb_en_buf & ((pc>=brk_addr0) & (pc<=brk_addr1)) & + brk_ctl[`BRK_RANGE] & `HWBRK_RANGE; + + +// Detect accesses +//--------------------------- + +// Detect Instruction read access +wire i_addr0_rd = equ_i_addr0 & brk_ctl[`BRK_I_EN]; +wire i_addr1_rd = equ_i_addr1 & brk_ctl[`BRK_I_EN]; +wire i_range_rd = equ_i_range & brk_ctl[`BRK_I_EN]; + +// Detect Execution-Unit write access +wire d_addr0_wr = equ_d_addr0 & ~brk_ctl[`BRK_I_EN] & |eu_mb_wr; +wire d_addr1_wr = equ_d_addr1 & ~brk_ctl[`BRK_I_EN] & |eu_mb_wr; +wire d_range_wr = equ_d_range & ~brk_ctl[`BRK_I_EN] & |eu_mb_wr; + +// Detect DATA read access +// Whenever an "ADD r9. &0x200" instruction is executed, &0x200 will be read +// before being written back. In that case, the read flag should not be set. +// In general, We should here make sure no write access occures during the +// same instruction cycle before setting the read flag. +reg [2:0] d_rd_trig; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) d_rd_trig <= 3'h0; + else if (exec_done) d_rd_trig <= 3'h0; + else d_rd_trig <= {equ_d_range & ~brk_ctl[`BRK_I_EN] & ~|eu_mb_wr, + equ_d_addr1 & ~brk_ctl[`BRK_I_EN] & ~|eu_mb_wr, + equ_d_addr0 & ~brk_ctl[`BRK_I_EN] & ~|eu_mb_wr}; + +wire d_addr0_rd = d_rd_trig[0] & exec_done & ~d_addr0_wr; +wire d_addr1_rd = d_rd_trig[1] & exec_done & ~d_addr1_wr; +wire d_range_rd = d_rd_trig[2] & exec_done & ~d_range_wr; + + +// Set flags +assign addr0_rd_set = brk_ctl[`BRK_MODE_RD] & (d_addr0_rd | i_addr0_rd); +assign addr0_wr_set = brk_ctl[`BRK_MODE_WR] & d_addr0_wr; +assign addr1_rd_set = brk_ctl[`BRK_MODE_RD] & (d_addr1_rd | i_addr1_rd); +assign addr1_wr_set = brk_ctl[`BRK_MODE_WR] & d_addr1_wr; +assign range_rd_set = brk_ctl[`BRK_MODE_RD] & (d_range_rd | i_range_rd); +assign range_wr_set = brk_ctl[`BRK_MODE_WR] & d_range_wr; + + +// Break CPU +assign brk_halt = brk_ctl[`BRK_EN] & |brk_stat_set; + + +endmodule // omsp_dbg_hwbrk + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_dbg_uart.v b/tests/openmsp430/rtl/omsp_dbg_uart.v new file mode 100644 index 000000000..319099a5f --- /dev/null +++ b/tests/openmsp430/rtl/omsp_dbg_uart.v @@ -0,0 +1,298 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_dbg_uart.v +// +// *Module Description: +// Debug UART communication interface (8N1, Half-duplex) +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_dbg_uart ( + +// OUTPUTs + dbg_addr, // Debug register address + dbg_din, // Debug register data input + dbg_rd, // Debug register data read + dbg_uart_txd, // Debug interface: UART TXD + dbg_wr, // Debug register data write + +// INPUTs + dbg_clk, // Debug unit clock + dbg_dout, // Debug register data output + dbg_rd_rdy, // Debug register data is ready for read + dbg_rst, // Debug unit reset + dbg_uart_rxd, // Debug interface: UART RXD + mem_burst, // Burst on going + mem_burst_end, // End TX/RX burst + mem_burst_rd, // Start TX burst + mem_burst_wr, // Start RX burst + mem_bw // Burst byte width +); + +// OUTPUTs +//========= +output [5:0] dbg_addr; // Debug register address +output [15:0] dbg_din; // Debug register data input +output dbg_rd; // Debug register data read +output dbg_uart_txd; // Debug interface: UART TXD +output dbg_wr; // Debug register data write + +// INPUTs +//========= +input dbg_clk; // Debug unit clock +input [15:0] dbg_dout; // Debug register data output +input dbg_rd_rdy; // Debug register data is ready for read +input dbg_rst; // Debug unit reset +input dbg_uart_rxd; // Debug interface: UART RXD +input mem_burst; // Burst on going +input mem_burst_end; // End TX/RX burst +input mem_burst_rd; // Start TX burst +input mem_burst_wr; // Start RX burst +input mem_bw; // Burst byte width + + +//============================================================================= +// 1) UART RECEIVE LINE SYNCHRONIZTION & FILTERING +//============================================================================= + +// Synchronize RXD input +//-------------------------------- +`ifdef SYNC_DBG_UART_RXD + + wire uart_rxd_n; + + omsp_sync_cell sync_cell_uart_rxd ( + .data_out (uart_rxd_n), + .data_in (~dbg_uart_rxd), + .clk (dbg_clk), + .rst (dbg_rst) + ); + wire uart_rxd = ~uart_rxd_n; +`else + wire uart_rxd = dbg_uart_rxd; +`endif + +// RXD input buffer +//-------------------------------- +reg [1:0] rxd_buf; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) rxd_buf <= 2'h3; + else rxd_buf <= {rxd_buf[0], uart_rxd}; + +// Majority decision +//------------------------ +reg rxd_maj; + +wire rxd_maj_nxt = (uart_rxd & rxd_buf[0]) | + (uart_rxd & rxd_buf[1]) | + (rxd_buf[0] & rxd_buf[1]); + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) rxd_maj <= 1'b1; + else rxd_maj <= rxd_maj_nxt; + +wire rxd_s = rxd_maj; +wire rxd_fe = rxd_maj & ~rxd_maj_nxt; +wire rxd_re = ~rxd_maj & rxd_maj_nxt; +wire rxd_edge = rxd_maj ^ rxd_maj_nxt; + +//============================================================================= +// 2) UART STATE MACHINE +//============================================================================= + +// Receive state +//------------------------ +reg [2:0] uart_state; +reg [2:0] uart_state_nxt; + +wire sync_done; +wire xfer_done; +reg [19:0] xfer_buf; +wire [19:0] xfer_buf_nxt; + +// State machine definition +parameter RX_SYNC = 3'h0; +parameter RX_CMD = 3'h1; +parameter RX_DATA1 = 3'h2; +parameter RX_DATA2 = 3'h3; +parameter TX_DATA1 = 3'h4; +parameter TX_DATA2 = 3'h5; + +// State transition +always @(uart_state or xfer_buf_nxt or mem_burst or mem_burst_wr or mem_burst_rd or mem_burst_end or mem_bw) + case (uart_state) + RX_SYNC : uart_state_nxt = RX_CMD; + RX_CMD : uart_state_nxt = mem_burst_wr ? + (mem_bw ? RX_DATA2 : RX_DATA1) : + mem_burst_rd ? + (mem_bw ? TX_DATA2 : TX_DATA1) : + (xfer_buf_nxt[`DBG_UART_WR] ? + (xfer_buf_nxt[`DBG_UART_BW] ? RX_DATA2 : RX_DATA1) : + (xfer_buf_nxt[`DBG_UART_BW] ? TX_DATA2 : TX_DATA1)); + RX_DATA1 : uart_state_nxt = RX_DATA2; + RX_DATA2 : uart_state_nxt = (mem_burst & ~mem_burst_end) ? + (mem_bw ? RX_DATA2 : RX_DATA1) : + RX_CMD; + TX_DATA1 : uart_state_nxt = TX_DATA2; + TX_DATA2 : uart_state_nxt = (mem_burst & ~mem_burst_end) ? + (mem_bw ? TX_DATA2 : TX_DATA1) : + RX_CMD; + // pragma coverage off + default : uart_state_nxt = RX_CMD; + // pragma coverage on + endcase + +// State machine +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) uart_state <= RX_SYNC; + else if (xfer_done | sync_done | + mem_burst_wr | mem_burst_rd) uart_state <= uart_state_nxt; + +// Utility signals +wire cmd_valid = (uart_state==RX_CMD) & xfer_done; +wire rx_active = (uart_state==RX_DATA1) | (uart_state==RX_DATA2) | (uart_state==RX_CMD); +wire tx_active = (uart_state==TX_DATA1) | (uart_state==TX_DATA2); + + +//============================================================================= +// 3) UART SYNCHRONIZATION +//============================================================================= +// After DBG_RST, the host needs to fist send a synchronization character (0x80) +// If this feature doesn't work properly, it is possible to disable it by +// commenting the DBG_UART_AUTO_SYNC define in the openMSP430.inc file. + +reg sync_busy; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) sync_busy <= 1'b0; + else if ((uart_state==RX_SYNC) & rxd_fe) sync_busy <= 1'b1; + else if ((uart_state==RX_SYNC) & rxd_re) sync_busy <= 1'b0; + +assign sync_done = (uart_state==RX_SYNC) & rxd_re & sync_busy; + +`ifdef DBG_UART_AUTO_SYNC + +reg [`DBG_UART_XFER_CNT_W+2:0] sync_cnt; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) sync_cnt <= {{`DBG_UART_XFER_CNT_W{1'b1}}, 3'b000}; + else if (sync_busy | (~sync_busy & sync_cnt[2])) sync_cnt <= sync_cnt+{{`DBG_UART_XFER_CNT_W+2{1'b0}}, 1'b1}; + +wire [`DBG_UART_XFER_CNT_W-1:0] bit_cnt_max = sync_cnt[`DBG_UART_XFER_CNT_W+2:3]; +`else +wire [`DBG_UART_XFER_CNT_W-1:0] bit_cnt_max = `DBG_UART_CNT; +`endif + + +//============================================================================= +// 4) UART RECEIVE / TRANSMIT +//============================================================================= + +// Transfer counter +//------------------------ +reg [3:0] xfer_bit; +reg [`DBG_UART_XFER_CNT_W-1:0] xfer_cnt; + +wire txd_start = dbg_rd_rdy | (xfer_done & (uart_state==TX_DATA1)); +wire rxd_start = (xfer_bit==4'h0) & rxd_fe & ((uart_state!=RX_SYNC)); +wire xfer_bit_inc = (xfer_bit!=4'h0) & (xfer_cnt=={`DBG_UART_XFER_CNT_W{1'b0}}); +assign xfer_done = rx_active ? (xfer_bit==4'ha) : (xfer_bit==4'hb); + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) xfer_bit <= 4'h0; + else if (txd_start | rxd_start) xfer_bit <= 4'h1; + else if (xfer_done) xfer_bit <= 4'h0; + else if (xfer_bit_inc) xfer_bit <= xfer_bit+4'h1; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) xfer_cnt <= {`DBG_UART_XFER_CNT_W{1'b0}}; + else if (rx_active & rxd_edge) xfer_cnt <= {1'b0, bit_cnt_max[`DBG_UART_XFER_CNT_W-1:1]}; + else if (txd_start | xfer_bit_inc) xfer_cnt <= bit_cnt_max; + else if (|xfer_cnt) xfer_cnt <= xfer_cnt+{`DBG_UART_XFER_CNT_W{1'b1}}; + + +// Receive/Transmit buffer +//------------------------- +assign xfer_buf_nxt = {rxd_s, xfer_buf[19:1]}; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) xfer_buf <= 20'h00000; + else if (dbg_rd_rdy) xfer_buf <= {1'b1, dbg_dout[15:8], 2'b01, dbg_dout[7:0], 1'b0}; + else if (xfer_bit_inc) xfer_buf <= xfer_buf_nxt; + + +// Generate TXD output +//------------------------ +reg dbg_uart_txd; + +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) dbg_uart_txd <= 1'b1; + else if (xfer_bit_inc & tx_active) dbg_uart_txd <= xfer_buf[0]; + + +//============================================================================= +// 5) INTERFACE TO DEBUG REGISTERS +//============================================================================= + +reg [5:0] dbg_addr; + always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) dbg_addr <= 6'h00; + else if (cmd_valid) dbg_addr <= xfer_buf_nxt[`DBG_UART_ADDR]; + +reg dbg_bw; +always @ (posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) dbg_bw <= 1'b0; + else if (cmd_valid) dbg_bw <= xfer_buf_nxt[`DBG_UART_BW]; + +wire dbg_din_bw = mem_burst ? mem_bw : dbg_bw; + +wire [15:0] dbg_din = dbg_din_bw ? {8'h00, xfer_buf_nxt[18:11]} : + {xfer_buf_nxt[18:11], xfer_buf_nxt[9:2]}; +wire dbg_wr = (xfer_done & (uart_state==RX_DATA2)); +wire dbg_rd = mem_burst ? (xfer_done & (uart_state==TX_DATA2)) : + (cmd_valid & ~xfer_buf_nxt[`DBG_UART_WR]) | mem_burst_rd; + + + +endmodule // omsp_dbg_uart + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_execution_unit.v b/tests/openmsp430/rtl/omsp_execution_unit.v new file mode 100644 index 000000000..8a2965e5c --- /dev/null +++ b/tests/openmsp430/rtl/omsp_execution_unit.v @@ -0,0 +1,420 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_execution_unit.v +// +// *Module Description: +// openMSP430 Execution unit +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_execution_unit ( + +// OUTPUTs + cpuoff, // Turns off the CPU + dbg_reg_din, // Debug unit CPU register data input + gie, // General interrupt enable + mab, // Memory address bus + mb_en, // Memory bus enable + mb_wr, // Memory bus write transfer + mdb_out, // Memory data bus output + oscoff, // Turns off LFXT1 clock input + pc_sw, // Program counter software value + pc_sw_wr, // Program counter software write + scg0, // System clock generator 1. Turns off the DCO + scg1, // System clock generator 1. Turns off the SMCLK + +// INPUTs + dbg_halt_st, // Halt/Run status from CPU + dbg_mem_dout, // Debug unit data output + dbg_reg_wr, // Debug unit CPU register write + e_state, // Execution state + exec_done, // Execution completed + inst_ad, // Decoded Inst: destination addressing mode + inst_as, // Decoded Inst: source addressing mode + inst_alu, // ALU control signals + inst_bw, // Decoded Inst: byte width + inst_dest, // Decoded Inst: destination (one hot) + inst_dext, // Decoded Inst: destination extended instruction word + inst_irq_rst, // Decoded Inst: reset interrupt + inst_jmp, // Decoded Inst: Conditional jump + inst_mov, // Decoded Inst: mov instruction + inst_sext, // Decoded Inst: source extended instruction word + inst_so, // Decoded Inst: Single-operand arithmetic + inst_src, // Decoded Inst: source (one hot) + inst_type, // Decoded Instruction type + mclk, // Main system clock + mdb_in, // Memory data bus input + pc, // Program counter + pc_nxt, // Next PC value (for CALL & IRQ) + puc_rst, // Main system reset + scan_enable // Scan enable (active during scan shifting) +); + +// OUTPUTs +//========= +output cpuoff; // Turns off the CPU +output [15:0] dbg_reg_din; // Debug unit CPU register data input +output gie; // General interrupt enable +output [15:0] mab; // Memory address bus +output mb_en; // Memory bus enable +output [1:0] mb_wr; // Memory bus write transfer +output [15:0] mdb_out; // Memory data bus output +output oscoff; // Turns off LFXT1 clock input +output [15:0] pc_sw; // Program counter software value +output pc_sw_wr; // Program counter software write +output scg0; // System clock generator 1. Turns off the DCO +output scg1; // System clock generator 1. Turns off the SMCLK + +// INPUTs +//========= +input dbg_halt_st; // Halt/Run status from CPU +input [15:0] dbg_mem_dout; // Debug unit data output +input dbg_reg_wr; // Debug unit CPU register write +input [3:0] e_state; // Execution state +input exec_done; // Execution completed +input [7:0] inst_ad; // Decoded Inst: destination addressing mode +input [7:0] inst_as; // Decoded Inst: source addressing mode +input [11:0] inst_alu; // ALU control signals +input inst_bw; // Decoded Inst: byte width +input [15:0] inst_dest; // Decoded Inst: destination (one hot) +input [15:0] inst_dext; // Decoded Inst: destination extended instruction word +input inst_irq_rst; // Decoded Inst: reset interrupt +input [7:0] inst_jmp; // Decoded Inst: Conditional jump +input inst_mov; // Decoded Inst: mov instruction +input [15:0] inst_sext; // Decoded Inst: source extended instruction word +input [7:0] inst_so; // Decoded Inst: Single-operand arithmetic +input [15:0] inst_src; // Decoded Inst: source (one hot) +input [2:0] inst_type; // Decoded Instruction type +input mclk; // Main system clock +input [15:0] mdb_in; // Memory data bus input +input [15:0] pc; // Program counter +input [15:0] pc_nxt; // Next PC value (for CALL & IRQ) +input puc_rst; // Main system reset +input scan_enable; // Scan enable (active during scan shifting) + + +//============================================================================= +// 1) INTERNAL WIRES/REGISTERS/PARAMETERS DECLARATION +//============================================================================= + +wire [15:0] alu_out; +wire [15:0] alu_out_add; +wire [3:0] alu_stat; +wire [3:0] alu_stat_wr; +wire [15:0] op_dst; +wire [15:0] op_src; +wire [15:0] reg_dest; +wire [15:0] reg_src; +wire [15:0] mdb_in_bw; +wire [15:0] mdb_in_val; +wire [3:0] status; + + +//============================================================================= +// 2) REGISTER FILE +//============================================================================= + +wire reg_dest_wr = ((e_state==`E_EXEC) & ( + (inst_type[`INST_TO] & inst_ad[`DIR] & ~inst_alu[`EXEC_NO_WR]) | + (inst_type[`INST_SO] & inst_as[`DIR] & ~(inst_so[`PUSH] | inst_so[`CALL] | inst_so[`RETI])) | + inst_type[`INST_JMP])) | dbg_reg_wr; + +wire reg_sp_wr = (((e_state==`E_IRQ_1) | (e_state==`E_IRQ_3)) & ~inst_irq_rst) | + ((e_state==`E_DST_RD) & ((inst_so[`PUSH] | inst_so[`CALL]) & ~inst_as[`IDX] & ~((inst_as[`INDIR] | inst_as[`INDIR_I]) & inst_src[1]))) | + ((e_state==`E_SRC_AD) & ((inst_so[`PUSH] | inst_so[`CALL]) & inst_as[`IDX])) | + ((e_state==`E_SRC_RD) & ((inst_so[`PUSH] | inst_so[`CALL]) & ((inst_as[`INDIR] | inst_as[`INDIR_I]) & inst_src[1]))); + +wire reg_sr_wr = (e_state==`E_DST_RD) & inst_so[`RETI]; + +wire reg_sr_clr = (e_state==`E_IRQ_2); + +wire reg_pc_call = ((e_state==`E_EXEC) & inst_so[`CALL]) | + ((e_state==`E_DST_WR) & inst_so[`RETI]); + +wire reg_incr = (exec_done & inst_as[`INDIR_I]) | + ((e_state==`E_SRC_RD) & inst_so[`RETI]) | + ((e_state==`E_EXEC) & inst_so[`RETI]); + +assign dbg_reg_din = reg_dest; + + +omsp_register_file register_file_0 ( + +// OUTPUTs + .cpuoff (cpuoff), // Turns off the CPU + .gie (gie), // General interrupt enable + .oscoff (oscoff), // Turns off LFXT1 clock input + .pc_sw (pc_sw), // Program counter software value + .pc_sw_wr (pc_sw_wr), // Program counter software write + .reg_dest (reg_dest), // Selected register destination content + .reg_src (reg_src), // Selected register source content + .scg0 (scg0), // System clock generator 1. Turns off the DCO + .scg1 (scg1), // System clock generator 1. Turns off the SMCLK + .status (status), // R2 Status {V,N,Z,C} + +// INPUTs + .alu_stat (alu_stat), // ALU Status {V,N,Z,C} + .alu_stat_wr (alu_stat_wr), // ALU Status write {V,N,Z,C} + .inst_bw (inst_bw), // Decoded Inst: byte width + .inst_dest (inst_dest), // Register destination selection + .inst_src (inst_src), // Register source selection + .mclk (mclk), // Main system clock + .pc (pc), // Program counter + .puc_rst (puc_rst), // Main system reset + .reg_dest_val (alu_out), // Selected register destination value + .reg_dest_wr (reg_dest_wr), // Write selected register destination + .reg_pc_call (reg_pc_call), // Trigger PC update for a CALL instruction + .reg_sp_val (alu_out_add), // Stack Pointer next value + .reg_sp_wr (reg_sp_wr), // Stack Pointer write + .reg_sr_clr (reg_sr_clr), // Status register clear for interrupts + .reg_sr_wr (reg_sr_wr), // Status Register update for RETI instruction + .reg_incr (reg_incr), // Increment source register + .scan_enable (scan_enable) // Scan enable (active during scan shifting) +); + + +//============================================================================= +// 3) SOURCE OPERAND MUXING +//============================================================================= +// inst_as[`DIR] : Register direct. -> Source is in register +// inst_as[`IDX] : Register indexed. -> Source is in memory, address is register+offset +// inst_as[`INDIR] : Register indirect. +// inst_as[`INDIR_I]: Register indirect autoincrement. +// inst_as[`SYMB] : Symbolic (operand is in memory at address PC+x). +// inst_as[`IMM] : Immediate (operand is next word in the instruction stream). +// inst_as[`ABS] : Absolute (operand is in memory at address x). +// inst_as[`CONST] : Constant. + +wire src_reg_src_sel = (e_state==`E_IRQ_0) | + (e_state==`E_IRQ_2) | + ((e_state==`E_SRC_RD) & ~inst_as[`ABS]) | + ((e_state==`E_SRC_WR) & ~inst_as[`ABS]) | + ((e_state==`E_EXEC) & inst_as[`DIR] & ~inst_type[`INST_JMP]); + +wire src_reg_dest_sel = (e_state==`E_IRQ_1) | + (e_state==`E_IRQ_3) | + ((e_state==`E_DST_RD) & (inst_so[`PUSH] | inst_so[`CALL])) | + ((e_state==`E_SRC_AD) & (inst_so[`PUSH] | inst_so[`CALL]) & inst_as[`IDX]); + +wire src_mdb_in_val_sel = ((e_state==`E_DST_RD) & inst_so[`RETI]) | + ((e_state==`E_EXEC) & (inst_as[`INDIR] | inst_as[`INDIR_I] | + inst_as[`IDX] | inst_as[`SYMB] | + inst_as[`ABS])); + +wire src_inst_dext_sel = ((e_state==`E_DST_RD) & ~(inst_so[`PUSH] | inst_so[`CALL])) | + ((e_state==`E_DST_WR) & ~(inst_so[`PUSH] | inst_so[`CALL] | + inst_so[`RETI])); + +wire src_inst_sext_sel = ((e_state==`E_EXEC) & (inst_type[`INST_JMP] | inst_as[`IMM] | + inst_as[`CONST] | inst_so[`RETI])); + + +assign op_src = src_reg_src_sel ? reg_src : + src_reg_dest_sel ? reg_dest : + src_mdb_in_val_sel ? mdb_in_val : + src_inst_dext_sel ? inst_dext : + src_inst_sext_sel ? inst_sext : 16'h0000; + + +//============================================================================= +// 4) DESTINATION OPERAND MUXING +//============================================================================= +// inst_ad[`DIR] : Register direct. +// inst_ad[`IDX] : Register indexed. +// inst_ad[`SYMB] : Symbolic (operand is in memory at address PC+x). +// inst_ad[`ABS] : Absolute (operand is in memory at address x). + + +wire dst_inst_sext_sel = ((e_state==`E_SRC_RD) & (inst_as[`IDX] | inst_as[`SYMB] | + inst_as[`ABS])) | + ((e_state==`E_SRC_WR) & (inst_as[`IDX] | inst_as[`SYMB] | + inst_as[`ABS])); + +wire dst_mdb_in_bw_sel = ((e_state==`E_DST_WR) & inst_so[`RETI]) | + ((e_state==`E_EXEC) & ~(inst_ad[`DIR] | inst_type[`INST_JMP] | + inst_type[`INST_SO]) & ~inst_so[`RETI]); + +wire dst_fffe_sel = (e_state==`E_IRQ_0) | + (e_state==`E_IRQ_1) | + (e_state==`E_IRQ_3) | + ((e_state==`E_DST_RD) & (inst_so[`PUSH] | inst_so[`CALL]) & ~inst_so[`RETI]) | + ((e_state==`E_SRC_AD) & (inst_so[`PUSH] | inst_so[`CALL]) & inst_as[`IDX]) | + ((e_state==`E_SRC_RD) & (inst_so[`PUSH] | inst_so[`CALL]) & (inst_as[`INDIR] | inst_as[`INDIR_I]) & inst_src[1]); + +wire dst_reg_dest_sel = ((e_state==`E_DST_RD) & ~(inst_so[`PUSH] | inst_so[`CALL] | inst_ad[`ABS] | inst_so[`RETI])) | + ((e_state==`E_DST_WR) & ~inst_ad[`ABS]) | + ((e_state==`E_EXEC) & (inst_ad[`DIR] | inst_type[`INST_JMP] | + inst_type[`INST_SO]) & ~inst_so[`RETI]); + + +assign op_dst = dbg_halt_st ? dbg_mem_dout : + dst_inst_sext_sel ? inst_sext : + dst_mdb_in_bw_sel ? mdb_in_bw : + dst_reg_dest_sel ? reg_dest : + dst_fffe_sel ? 16'hfffe : 16'h0000; + + +//============================================================================= +// 5) ALU +//============================================================================= + +wire exec_cycle = (e_state==`E_EXEC); + +omsp_alu alu_0 ( + +// OUTPUTs + .alu_out (alu_out), // ALU output value + .alu_out_add (alu_out_add), // ALU adder output value + .alu_stat (alu_stat), // ALU Status {V,N,Z,C} + .alu_stat_wr (alu_stat_wr), // ALU Status write {V,N,Z,C} + +// INPUTs + .dbg_halt_st (dbg_halt_st), // Halt/Run status from CPU + .exec_cycle (exec_cycle), // Instruction execution cycle + .inst_alu (inst_alu), // ALU control signals + .inst_bw (inst_bw), // Decoded Inst: byte width + .inst_jmp (inst_jmp), // Decoded Inst: Conditional jump + .inst_so (inst_so), // Single-operand arithmetic + .op_dst (op_dst), // Destination operand + .op_src (op_src), // Source operand + .status (status) // R2 Status {V,N,Z,C} +); + + +//============================================================================= +// 6) MEMORY INTERFACE +//============================================================================= + +// Detect memory read/write access +assign mb_en = ((e_state==`E_IRQ_1) & ~inst_irq_rst) | + ((e_state==`E_IRQ_3) & ~inst_irq_rst) | + ((e_state==`E_SRC_RD) & ~inst_as[`IMM]) | + (e_state==`E_SRC_WR) | + ((e_state==`E_EXEC) & inst_so[`RETI]) | + ((e_state==`E_DST_RD) & ~inst_type[`INST_SO] + & ~inst_mov) | + (e_state==`E_DST_WR); + +wire [1:0] mb_wr_msk = inst_alu[`EXEC_NO_WR] ? 2'b00 : + ~inst_bw ? 2'b11 : + alu_out_add[0] ? 2'b10 : 2'b01; +assign mb_wr = ({2{(e_state==`E_IRQ_1)}} | + {2{(e_state==`E_IRQ_3)}} | + {2{(e_state==`E_DST_WR)}} | + {2{(e_state==`E_SRC_WR)}}) & mb_wr_msk; + +// Memory address bus +assign mab = alu_out_add[15:0]; + +// Memory data bus output +reg [15:0] mdb_out_nxt; + +`ifdef CLOCK_GATING +wire mdb_out_nxt_en = (e_state==`E_DST_RD) | + (((e_state==`E_EXEC) & ~inst_so[`CALL]) | + (e_state==`E_IRQ_0) | (e_state==`E_IRQ_2)); +wire mclk_mdb_out_nxt; +omsp_clock_gate clock_gate_mdb_out_nxt (.gclk(mclk_mdb_out_nxt), + .clk (mclk), .enable(mdb_out_nxt_en), .scan_enable(scan_enable)); +`else +wire mclk_mdb_out_nxt = mclk; +`endif + +always @(posedge mclk_mdb_out_nxt or posedge puc_rst) + if (puc_rst) mdb_out_nxt <= 16'h0000; + else if (e_state==`E_DST_RD) mdb_out_nxt <= pc_nxt; +`ifdef CLOCK_GATING + else mdb_out_nxt <= alu_out; +`else + else if ((e_state==`E_EXEC & ~inst_so[`CALL]) | + (e_state==`E_IRQ_0) | (e_state==`E_IRQ_2)) mdb_out_nxt <= alu_out; +`endif + +assign mdb_out = inst_bw ? {2{mdb_out_nxt[7:0]}} : mdb_out_nxt; + +// Format memory data bus input depending on BW +reg mab_lsb; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) mab_lsb <= 1'b0; + else if (mb_en) mab_lsb <= alu_out_add[0]; + +assign mdb_in_bw = ~inst_bw ? mdb_in : + mab_lsb ? {2{mdb_in[15:8]}} : mdb_in; + +// Memory data bus input buffer (buffer after a source read) +reg mdb_in_buf_en; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) mdb_in_buf_en <= 1'b0; + else mdb_in_buf_en <= (e_state==`E_SRC_RD); + +reg mdb_in_buf_valid; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) mdb_in_buf_valid <= 1'b0; + else if (e_state==`E_EXEC) mdb_in_buf_valid <= 1'b0; + else if (mdb_in_buf_en) mdb_in_buf_valid <= 1'b1; + +reg [15:0] mdb_in_buf; + +`ifdef CLOCK_GATING +wire mclk_mdb_in_buf; +omsp_clock_gate clock_gate_mdb_in_buf (.gclk(mclk_mdb_in_buf), + .clk (mclk), .enable(mdb_in_buf_en), .scan_enable(scan_enable)); +`else +wire mclk_mdb_in_buf = mclk; +`endif + +always @(posedge mclk_mdb_in_buf or posedge puc_rst) + if (puc_rst) mdb_in_buf <= 16'h0000; +`ifdef CLOCK_GATING + else mdb_in_buf <= mdb_in_bw; +`else + else if (mdb_in_buf_en) mdb_in_buf <= mdb_in_bw; +`endif + +assign mdb_in_val = mdb_in_buf_valid ? mdb_in_buf : mdb_in_bw; + + +endmodule // omsp_execution_unit + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_frontend.v b/tests/openmsp430/rtl/omsp_frontend.v new file mode 100644 index 000000000..343944bd2 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_frontend.v @@ -0,0 +1,966 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_frontend.v +// +// *Module Description: +// openMSP430 Instruction fetch and decode unit +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_frontend ( + +// OUTPUTs + dbg_halt_st, // Halt/Run status from CPU + decode_noirq, // Frontend decode instruction + e_state, // Execution state + exec_done, // Execution completed + inst_ad, // Decoded Inst: destination addressing mode + inst_as, // Decoded Inst: source addressing mode + inst_alu, // ALU control signals + inst_bw, // Decoded Inst: byte width + inst_dest, // Decoded Inst: destination (one hot) + inst_dext, // Decoded Inst: destination extended instruction word + inst_irq_rst, // Decoded Inst: Reset interrupt + inst_jmp, // Decoded Inst: Conditional jump + inst_mov, // Decoded Inst: mov instruction + inst_sext, // Decoded Inst: source extended instruction word + inst_so, // Decoded Inst: Single-operand arithmetic + inst_src, // Decoded Inst: source (one hot) + inst_type, // Decoded Instruction type + irq_acc, // Interrupt request accepted (one-hot signal) + mab, // Frontend Memory address bus + mb_en, // Frontend Memory bus enable + mclk_enable, // Main System Clock enable + mclk_wkup, // Main System Clock wake-up (asynchronous) + nmi_acc, // Non-Maskable interrupt request accepted + pc, // Program counter + pc_nxt, // Next PC value (for CALL & IRQ) + +// INPUTs + cpu_en_s, // Enable CPU code execution (synchronous) + cpuoff, // Turns off the CPU + dbg_halt_cmd, // Halt CPU command + dbg_reg_sel, // Debug selected register for rd/wr access + fe_pmem_wait, // Frontend wait for Instruction fetch + gie, // General interrupt enable + irq, // Maskable interrupts + mclk, // Main system clock + mdb_in, // Frontend Memory data bus input + nmi_pnd, // Non-maskable interrupt pending + nmi_wkup, // NMI Wakeup + pc_sw, // Program counter software value + pc_sw_wr, // Program counter software write + puc_rst, // Main system reset + scan_enable, // Scan enable (active during scan shifting) + wdt_irq, // Watchdog-timer interrupt + wdt_wkup, // Watchdog Wakeup + wkup // System Wake-up (asynchronous) +); + +// OUTPUTs +//========= +output dbg_halt_st; // Halt/Run status from CPU +output decode_noirq; // Frontend decode instruction +output [3:0] e_state; // Execution state +output exec_done; // Execution completed +output [7:0] inst_ad; // Decoded Inst: destination addressing mode +output [7:0] inst_as; // Decoded Inst: source addressing mode +output [11:0] inst_alu; // ALU control signals +output inst_bw; // Decoded Inst: byte width +output [15:0] inst_dest; // Decoded Inst: destination (one hot) +output [15:0] inst_dext; // Decoded Inst: destination extended instruction word +output inst_irq_rst; // Decoded Inst: Reset interrupt +output [7:0] inst_jmp; // Decoded Inst: Conditional jump +output inst_mov; // Decoded Inst: mov instruction +output [15:0] inst_sext; // Decoded Inst: source extended instruction word +output [7:0] inst_so; // Decoded Inst: Single-operand arithmetic +output [15:0] inst_src; // Decoded Inst: source (one hot) +output [2:0] inst_type; // Decoded Instruction type +output [13:0] irq_acc; // Interrupt request accepted (one-hot signal) +output [15:0] mab; // Frontend Memory address bus +output mb_en; // Frontend Memory bus enable +output mclk_enable; // Main System Clock enable +output mclk_wkup; // Main System Clock wake-up (asynchronous) +output nmi_acc; // Non-Maskable interrupt request accepted +output [15:0] pc; // Program counter +output [15:0] pc_nxt; // Next PC value (for CALL & IRQ) + +// INPUTs +//========= +input cpu_en_s; // Enable CPU code execution (synchronous) +input cpuoff; // Turns off the CPU +input dbg_halt_cmd; // Halt CPU command +input [3:0] dbg_reg_sel; // Debug selected register for rd/wr access +input fe_pmem_wait; // Frontend wait for Instruction fetch +input gie; // General interrupt enable +input [13:0] irq; // Maskable interrupts +input mclk; // Main system clock +input [15:0] mdb_in; // Frontend Memory data bus input +input nmi_pnd; // Non-maskable interrupt pending +input nmi_wkup; // NMI Wakeup +input [15:0] pc_sw; // Program counter software value +input pc_sw_wr; // Program counter software write +input puc_rst; // Main system reset +input scan_enable; // Scan enable (active during scan shifting) +input wdt_irq; // Watchdog-timer interrupt +input wdt_wkup; // Watchdog Wakeup +input wkup; // System Wake-up (asynchronous) + + +//============================================================================= +// 1) UTILITY FUNCTIONS +//============================================================================= + +// 16 bits one-hot decoder +function [15:0] one_hot16; + input [3:0] binary; + begin + one_hot16 = 16'h0000; + one_hot16[binary] = 1'b1; + end +endfunction + +// 8 bits one-hot decoder +function [7:0] one_hot8; + input [2:0] binary; + begin + one_hot8 = 8'h00; + one_hot8[binary] = 1'b1; + end +endfunction + + +//============================================================================= +// 2) PARAMETER DEFINITIONS +//============================================================================= + +// +// 2.1) Instruction State machine definitons +//------------------------------------------- + +parameter I_IRQ_FETCH = `I_IRQ_FETCH; +parameter I_IRQ_DONE = `I_IRQ_DONE; +parameter I_DEC = `I_DEC; // New instruction ready for decode +parameter I_EXT1 = `I_EXT1; // 1st Extension word +parameter I_EXT2 = `I_EXT2; // 2nd Extension word +parameter I_IDLE = `I_IDLE; // CPU is in IDLE mode + +// +// 2.2) Execution State machine definitons +//------------------------------------------- + +parameter E_IRQ_0 = `E_IRQ_0; +parameter E_IRQ_1 = `E_IRQ_1; +parameter E_IRQ_2 = `E_IRQ_2; +parameter E_IRQ_3 = `E_IRQ_3; +parameter E_IRQ_4 = `E_IRQ_4; +parameter E_SRC_AD = `E_SRC_AD; +parameter E_SRC_RD = `E_SRC_RD; +parameter E_SRC_WR = `E_SRC_WR; +parameter E_DST_AD = `E_DST_AD; +parameter E_DST_RD = `E_DST_RD; +parameter E_DST_WR = `E_DST_WR; +parameter E_EXEC = `E_EXEC; +parameter E_JUMP = `E_JUMP; +parameter E_IDLE = `E_IDLE; + + +//============================================================================= +// 3) FRONTEND STATE MACHINE +//============================================================================= + +// The wire "conv" is used as state bits to calculate the next response +reg [2:0] i_state; +reg [2:0] i_state_nxt; + +reg [1:0] inst_sz; +wire [1:0] inst_sz_nxt; +wire irq_detect; +wire [2:0] inst_type_nxt; +wire is_const; +reg [15:0] sconst_nxt; +reg [3:0] e_state_nxt; + +// CPU on/off through the debug interface or cpu_en port +wire cpu_halt_cmd = dbg_halt_cmd | ~cpu_en_s; + +// States Transitions +always @(i_state or inst_sz or inst_sz_nxt or pc_sw_wr or exec_done or + irq_detect or cpuoff or cpu_halt_cmd or e_state) + case(i_state) + I_IDLE : i_state_nxt = (irq_detect & ~cpu_halt_cmd) ? I_IRQ_FETCH : + (~cpuoff & ~cpu_halt_cmd) ? I_DEC : I_IDLE; + I_IRQ_FETCH: i_state_nxt = I_IRQ_DONE; + I_IRQ_DONE : i_state_nxt = I_DEC; + I_DEC : i_state_nxt = irq_detect ? I_IRQ_FETCH : + (cpuoff | cpu_halt_cmd) & exec_done ? I_IDLE : + cpu_halt_cmd & (e_state==E_IDLE) ? I_IDLE : + pc_sw_wr ? I_DEC : + ~exec_done & ~(e_state==E_IDLE) ? I_DEC : // Wait in decode state + (inst_sz_nxt!=2'b00) ? I_EXT1 : I_DEC; // until execution is completed + I_EXT1 : i_state_nxt = pc_sw_wr ? I_DEC : + (inst_sz!=2'b01) ? I_EXT2 : I_DEC; + I_EXT2 : i_state_nxt = I_DEC; + // pragma coverage off + default : i_state_nxt = I_IRQ_FETCH; + // pragma coverage on + endcase + +// State machine +always @(posedge mclk or posedge puc_rst) + if (puc_rst) i_state <= I_IRQ_FETCH; + else i_state <= i_state_nxt; + +// Utility signals +wire decode_noirq = ((i_state==I_DEC) & (exec_done | (e_state==E_IDLE))); +wire decode = decode_noirq | irq_detect; +wire fetch = ~((i_state==I_DEC) & ~(exec_done | (e_state==E_IDLE))) & ~(e_state_nxt==E_IDLE); + +// Debug interface cpu status +reg dbg_halt_st; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) dbg_halt_st <= 1'b0; + else dbg_halt_st <= cpu_halt_cmd & (i_state_nxt==I_IDLE); + + +//============================================================================= +// 4) INTERRUPT HANDLING & SYSTEM WAKEUP +//============================================================================= + +// +// 4.1) INTERRUPT HANDLING +//----------------------------------------- + +// Detect reset interrupt +reg inst_irq_rst; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) inst_irq_rst <= 1'b1; + else if (exec_done) inst_irq_rst <= 1'b0; + +// Detect other interrupts +assign irq_detect = (nmi_pnd | ((|irq | wdt_irq) & gie)) & ~cpu_halt_cmd & ~dbg_halt_st & (exec_done | (i_state==I_IDLE)); + +`ifdef CLOCK_GATING +wire mclk_irq_num; +omsp_clock_gate clock_gate_irq_num (.gclk(mclk_irq_num), + .clk (mclk), .enable(irq_detect), .scan_enable(scan_enable)); +`else +wire mclk_irq_num = mclk; +`endif + +// Select interrupt vector +reg [3:0] irq_num; +always @(posedge mclk_irq_num or posedge puc_rst) + if (puc_rst) irq_num <= 4'hf; +`ifdef CLOCK_GATING + else irq_num <= nmi_pnd ? 4'he : +`else + else if (irq_detect) irq_num <= nmi_pnd ? 4'he : +`endif + irq[13] ? 4'hd : + irq[12] ? 4'hc : + irq[11] ? 4'hb : + (irq[10] | wdt_irq) ? 4'ha : + irq[9] ? 4'h9 : + irq[8] ? 4'h8 : + irq[7] ? 4'h7 : + irq[6] ? 4'h6 : + irq[5] ? 4'h5 : + irq[4] ? 4'h4 : + irq[3] ? 4'h3 : + irq[2] ? 4'h2 : + irq[1] ? 4'h1 : + irq[0] ? 4'h0 : 4'hf; + +wire [15:0] irq_addr = {11'h7ff, irq_num, 1'b0}; + +// Interrupt request accepted +wire [15:0] irq_acc_all = one_hot16(irq_num) & {16{(i_state==I_IRQ_FETCH)}}; +wire [13:0] irq_acc = irq_acc_all[13:0]; +wire nmi_acc = irq_acc_all[14]; + +// +// 4.2) SYSTEM WAKEUP +//----------------------------------------- +`ifdef CPUOFF_EN + +// Generate the main system clock enable signal + // Keep the clock running if: +wire mclk_enable = inst_irq_rst ? cpu_en_s : // - the RESET interrupt is currently executing + // and if the CPU is enabled + // otherwise if: + ~((cpuoff | ~cpu_en_s) & // - the CPUOFF flag, cpu_en command, instruction + (i_state==I_IDLE) & // and execution state machines are all two + (e_state==E_IDLE)); // not idle. + + +// Wakeup condition from maskable interrupts +wire mirq_wkup; +omsp_and_gate and_mirq_wkup (.y(mirq_wkup), .a(wkup | wdt_wkup), .b(gie)); + +// Combined asynchronous wakeup detection from nmi & irq (masked if the cpu is disabled) +omsp_and_gate and_mclk_wkup (.y(mclk_wkup), .a(nmi_wkup | mirq_wkup), .b(cpu_en_s)); + +`else + +// In the CPUOFF feature is disabled, the wake-up and enable signals are always 1 +assign mclk_wkup = 1'b1; +assign mclk_enable = 1'b1; +`endif + +//============================================================================= +// 5) FETCH INSTRUCTION +//============================================================================= + +// +// 5.1) PROGRAM COUNTER & MEMORY INTERFACE +//----------------------------------------- + +// Program counter +reg [15:0] pc; + +// Compute next PC value +wire [15:0] pc_incr = pc + {14'h0000, fetch, 1'b0}; +wire [15:0] pc_nxt = pc_sw_wr ? pc_sw : + (i_state==I_IRQ_FETCH) ? irq_addr : + (i_state==I_IRQ_DONE) ? mdb_in : pc_incr; + +`ifdef CLOCK_GATING +wire pc_en = fetch | + pc_sw_wr | + (i_state==I_IRQ_FETCH) | + (i_state==I_IRQ_DONE); +wire mclk_pc; +omsp_clock_gate clock_gate_pc (.gclk(mclk_pc), + .clk (mclk), .enable(pc_en), .scan_enable(scan_enable)); +`else +wire mclk_pc = mclk; +`endif + +always @(posedge mclk_pc or posedge puc_rst) + if (puc_rst) pc <= 16'h0000; + else pc <= pc_nxt; + +// Check if ROM has been busy in order to retry ROM access +reg pmem_busy; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) pmem_busy <= 1'b0; + else pmem_busy <= fe_pmem_wait; + +// Memory interface +wire [15:0] mab = pc_nxt; +wire mb_en = fetch | pc_sw_wr | (i_state==I_IRQ_FETCH) | pmem_busy | (dbg_halt_st & ~cpu_halt_cmd); + + +// +// 5.2) INSTRUCTION REGISTER +//-------------------------------- + +// Instruction register +wire [15:0] ir = mdb_in; + +// Detect if source extension word is required +wire is_sext = (inst_as[`IDX] | inst_as[`SYMB] | inst_as[`ABS] | inst_as[`IMM]); + +// For the Symbolic addressing mode, add -2 to the extension word in order +// to make up for the PC address +wire [15:0] ext_incr = ((i_state==I_EXT1) & inst_as[`SYMB]) | + ((i_state==I_EXT2) & inst_ad[`SYMB]) | + ((i_state==I_EXT1) & ~inst_as[`SYMB] & + ~(i_state_nxt==I_EXT2) & inst_ad[`SYMB]) ? 16'hfffe : 16'h0000; + +wire [15:0] ext_nxt = ir + ext_incr; + +// Store source extension word +reg [15:0] inst_sext; + +`ifdef CLOCK_GATING +wire inst_sext_en = (decode & is_const) | + (decode & inst_type_nxt[`INST_JMP]) | + ((i_state==I_EXT1) & is_sext); +wire mclk_inst_sext; +omsp_clock_gate clock_gate_inst_sext (.gclk(mclk_inst_sext), + .clk (mclk), .enable(inst_sext_en), .scan_enable(scan_enable)); +`else +wire mclk_inst_sext = mclk; +`endif + +always @(posedge mclk_inst_sext or posedge puc_rst) + if (puc_rst) inst_sext <= 16'h0000; + else if (decode & is_const) inst_sext <= sconst_nxt; + else if (decode & inst_type_nxt[`INST_JMP]) inst_sext <= {{5{ir[9]}},ir[9:0],1'b0}; +`ifdef CLOCK_GATING + else inst_sext <= ext_nxt; +`else + else if ((i_state==I_EXT1) & is_sext) inst_sext <= ext_nxt; +`endif + +// Source extension word is ready +wire inst_sext_rdy = (i_state==I_EXT1) & is_sext; + + +// Store destination extension word +reg [15:0] inst_dext; + +`ifdef CLOCK_GATING +wire inst_dext_en = ((i_state==I_EXT1) & ~is_sext) | + (i_state==I_EXT2); +wire mclk_inst_dext; +omsp_clock_gate clock_gate_inst_dext (.gclk(mclk_inst_dext), + .clk (mclk), .enable(inst_dext_en), .scan_enable(scan_enable)); +`else +wire mclk_inst_dext = mclk; +`endif + +always @(posedge mclk_inst_dext or posedge puc_rst) + if (puc_rst) inst_dext <= 16'h0000; + else if ((i_state==I_EXT1) & ~is_sext) inst_dext <= ext_nxt; +`ifdef CLOCK_GATING + else inst_dext <= ext_nxt; +`else + else if (i_state==I_EXT2) inst_dext <= ext_nxt; +`endif + +// Destination extension word is ready +wire inst_dext_rdy = (((i_state==I_EXT1) & ~is_sext) | (i_state==I_EXT2)); + + +//============================================================================= +// 6) DECODE INSTRUCTION +//============================================================================= + +`ifdef CLOCK_GATING +wire mclk_decode; +omsp_clock_gate clock_gate_decode (.gclk(mclk_decode), + .clk (mclk), .enable(decode), .scan_enable(scan_enable)); +`else +wire mclk_decode = mclk; +`endif + +// +// 6.1) OPCODE: INSTRUCTION TYPE +//---------------------------------------- +// Instructions type is encoded in a one hot fashion as following: +// +// 3'b001: Single-operand arithmetic +// 3'b010: Conditional jump +// 3'b100: Two-operand arithmetic + +reg [2:0] inst_type; +assign inst_type_nxt = {(ir[15:14]!=2'b00), + (ir[15:13]==3'b001), + (ir[15:13]==3'b000)} & {3{~irq_detect}}; + +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_type <= 3'b000; +`ifdef CLOCK_GATING + else inst_type <= inst_type_nxt; +`else + else if (decode) inst_type <= inst_type_nxt; +`endif + +// +// 6.2) OPCODE: SINGLE-OPERAND ARITHMETIC +//---------------------------------------- +// Instructions are encoded in a one hot fashion as following: +// +// 8'b00000001: RRC +// 8'b00000010: SWPB +// 8'b00000100: RRA +// 8'b00001000: SXT +// 8'b00010000: PUSH +// 8'b00100000: CALL +// 8'b01000000: RETI +// 8'b10000000: IRQ + +reg [7:0] inst_so; +wire [7:0] inst_so_nxt = irq_detect ? 8'h80 : (one_hot8(ir[9:7]) & {8{inst_type_nxt[`INST_SO]}}); + +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_so <= 8'h00; +`ifdef CLOCK_GATING + else inst_so <= inst_so_nxt; +`else + else if (decode) inst_so <= inst_so_nxt; +`endif + +// +// 6.3) OPCODE: CONDITIONAL JUMP +//-------------------------------- +// Instructions are encoded in a one hot fashion as following: +// +// 8'b00000001: JNE/JNZ +// 8'b00000010: JEQ/JZ +// 8'b00000100: JNC/JLO +// 8'b00001000: JC/JHS +// 8'b00010000: JN +// 8'b00100000: JGE +// 8'b01000000: JL +// 8'b10000000: JMP + +reg [2:0] inst_jmp_bin; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_jmp_bin <= 3'h0; +`ifdef CLOCK_GATING + else inst_jmp_bin <= ir[12:10]; +`else + else if (decode) inst_jmp_bin <= ir[12:10]; +`endif + +wire [7:0] inst_jmp = one_hot8(inst_jmp_bin) & {8{inst_type[`INST_JMP]}}; + + +// +// 6.4) OPCODE: TWO-OPERAND ARITHMETIC +//------------------------------------- +// Instructions are encoded in a one hot fashion as following: +// +// 12'b000000000001: MOV +// 12'b000000000010: ADD +// 12'b000000000100: ADDC +// 12'b000000001000: SUBC +// 12'b000000010000: SUB +// 12'b000000100000: CMP +// 12'b000001000000: DADD +// 12'b000010000000: BIT +// 12'b000100000000: BIC +// 12'b001000000000: BIS +// 12'b010000000000: XOR +// 12'b100000000000: AND + +wire [15:0] inst_to_1hot = one_hot16(ir[15:12]) & {16{inst_type_nxt[`INST_TO]}}; +wire [11:0] inst_to_nxt = inst_to_1hot[15:4]; + +reg inst_mov; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_mov <= 1'b0; +`ifdef CLOCK_GATING + else inst_mov <= inst_to_nxt[`MOV]; +`else + else if (decode) inst_mov <= inst_to_nxt[`MOV]; +`endif + + +// +// 6.5) SOURCE AND DESTINATION REGISTERS +//--------------------------------------- + +// Destination register +reg [3:0] inst_dest_bin; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_dest_bin <= 4'h0; +`ifdef CLOCK_GATING + else inst_dest_bin <= ir[3:0]; +`else + else if (decode) inst_dest_bin <= ir[3:0]; +`endif + +wire [15:0] inst_dest = dbg_halt_st ? one_hot16(dbg_reg_sel) : + inst_type[`INST_JMP] ? 16'h0001 : + inst_so[`IRQ] | + inst_so[`PUSH] | + inst_so[`CALL] ? 16'h0002 : + one_hot16(inst_dest_bin); + + +// Source register +reg [3:0] inst_src_bin; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_src_bin <= 4'h0; +`ifdef CLOCK_GATING + else inst_src_bin <= ir[11:8]; +`else + else if (decode) inst_src_bin <= ir[11:8]; +`endif + +wire [15:0] inst_src = inst_type[`INST_TO] ? one_hot16(inst_src_bin) : + inst_so[`RETI] ? 16'h0002 : + inst_so[`IRQ] ? 16'h0001 : + inst_type[`INST_SO] ? one_hot16(inst_dest_bin) : 16'h0000; + + +// +// 6.6) SOURCE ADDRESSING MODES +//-------------------------------- +// Source addressing modes are encoded in a one hot fashion as following: +// +// 13'b0000000000001: Register direct. +// 13'b0000000000010: Register indexed. +// 13'b0000000000100: Register indirect. +// 13'b0000000001000: Register indirect autoincrement. +// 13'b0000000010000: Symbolic (operand is in memory at address PC+x). +// 13'b0000000100000: Immediate (operand is next word in the instruction stream). +// 13'b0000001000000: Absolute (operand is in memory at address x). +// 13'b0000010000000: Constant 4. +// 13'b0000100000000: Constant 8. +// 13'b0001000000000: Constant 0. +// 13'b0010000000000: Constant 1. +// 13'b0100000000000: Constant 2. +// 13'b1000000000000: Constant -1. + +reg [12:0] inst_as_nxt; + +wire [3:0] src_reg = inst_type_nxt[`INST_SO] ? ir[3:0] : ir[11:8]; + +always @(src_reg or ir or inst_type_nxt) + begin + if (inst_type_nxt[`INST_JMP]) + inst_as_nxt = 13'b0000000000001; + else if (src_reg==4'h3) // Addressing mode using R3 + case (ir[5:4]) + 2'b11 : inst_as_nxt = 13'b1000000000000; + 2'b10 : inst_as_nxt = 13'b0100000000000; + 2'b01 : inst_as_nxt = 13'b0010000000000; + default: inst_as_nxt = 13'b0001000000000; + endcase + else if (src_reg==4'h2) // Addressing mode using R2 + case (ir[5:4]) + 2'b11 : inst_as_nxt = 13'b0000100000000; + 2'b10 : inst_as_nxt = 13'b0000010000000; + 2'b01 : inst_as_nxt = 13'b0000001000000; + default: inst_as_nxt = 13'b0000000000001; + endcase + else if (src_reg==4'h0) // Addressing mode using R0 + case (ir[5:4]) + 2'b11 : inst_as_nxt = 13'b0000000100000; + 2'b10 : inst_as_nxt = 13'b0000000000100; + 2'b01 : inst_as_nxt = 13'b0000000010000; + default: inst_as_nxt = 13'b0000000000001; + endcase + else // General Addressing mode + case (ir[5:4]) + 2'b11 : inst_as_nxt = 13'b0000000001000; + 2'b10 : inst_as_nxt = 13'b0000000000100; + 2'b01 : inst_as_nxt = 13'b0000000000010; + default: inst_as_nxt = 13'b0000000000001; + endcase + end +assign is_const = |inst_as_nxt[12:7]; + +reg [7:0] inst_as; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_as <= 8'h00; +`ifdef CLOCK_GATING + else inst_as <= {is_const, inst_as_nxt[6:0]}; +`else + else if (decode) inst_as <= {is_const, inst_as_nxt[6:0]}; +`endif + + +// 13'b0000010000000: Constant 4. +// 13'b0000100000000: Constant 8. +// 13'b0001000000000: Constant 0. +// 13'b0010000000000: Constant 1. +// 13'b0100000000000: Constant 2. +// 13'b1000000000000: Constant -1. +always @(inst_as_nxt) + begin + if (inst_as_nxt[7]) sconst_nxt = 16'h0004; + else if (inst_as_nxt[8]) sconst_nxt = 16'h0008; + else if (inst_as_nxt[9]) sconst_nxt = 16'h0000; + else if (inst_as_nxt[10]) sconst_nxt = 16'h0001; + else if (inst_as_nxt[11]) sconst_nxt = 16'h0002; + else if (inst_as_nxt[12]) sconst_nxt = 16'hffff; + else sconst_nxt = 16'h0000; + end + + +// +// 6.7) DESTINATION ADDRESSING MODES +//----------------------------------- +// Destination addressing modes are encoded in a one hot fashion as following: +// +// 8'b00000001: Register direct. +// 8'b00000010: Register indexed. +// 8'b00010000: Symbolic (operand is in memory at address PC+x). +// 8'b01000000: Absolute (operand is in memory at address x). + +reg [7:0] inst_ad_nxt; + +wire [3:0] dest_reg = ir[3:0]; + +always @(dest_reg or ir or inst_type_nxt) + begin + if (~inst_type_nxt[`INST_TO]) + inst_ad_nxt = 8'b00000000; + else if (dest_reg==4'h2) // Addressing mode using R2 + case (ir[7]) + 1'b1 : inst_ad_nxt = 8'b01000000; + default: inst_ad_nxt = 8'b00000001; + endcase + else if (dest_reg==4'h0) // Addressing mode using R0 + case (ir[7]) + 1'b1 : inst_ad_nxt = 8'b00010000; + default: inst_ad_nxt = 8'b00000001; + endcase + else // General Addressing mode + case (ir[7]) + 1'b1 : inst_ad_nxt = 8'b00000010; + default: inst_ad_nxt = 8'b00000001; + endcase + end + +reg [7:0] inst_ad; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_ad <= 8'h00; +`ifdef CLOCK_GATING + else inst_ad <= inst_ad_nxt; +`else + else if (decode) inst_ad <= inst_ad_nxt; +`endif + + +// +// 6.8) REMAINING INSTRUCTION DECODING +//------------------------------------- + +// Operation size +reg inst_bw; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) inst_bw <= 1'b0; + else if (decode) inst_bw <= ir[6] & ~inst_type_nxt[`INST_JMP] & ~irq_detect & ~cpu_halt_cmd; + +// Extended instruction size +assign inst_sz_nxt = {1'b0, (inst_as_nxt[`IDX] | inst_as_nxt[`SYMB] | inst_as_nxt[`ABS] | inst_as_nxt[`IMM])} + + {1'b0, ((inst_ad_nxt[`IDX] | inst_ad_nxt[`SYMB] | inst_ad_nxt[`ABS]) & ~inst_type_nxt[`INST_SO])}; +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_sz <= 2'b00; +`ifdef CLOCK_GATING + else inst_sz <= inst_sz_nxt; +`else + else if (decode) inst_sz <= inst_sz_nxt; +`endif + + +//============================================================================= +// 7) EXECUTION-UNIT STATE MACHINE +//============================================================================= + +// State machine registers +reg [3:0] e_state; + + +// State machine control signals +//-------------------------------- + +wire src_acalc_pre = inst_as_nxt[`IDX] | inst_as_nxt[`SYMB] | inst_as_nxt[`ABS]; +wire src_rd_pre = inst_as_nxt[`INDIR] | inst_as_nxt[`INDIR_I] | inst_as_nxt[`IMM] | inst_so_nxt[`RETI]; +wire dst_acalc_pre = inst_ad_nxt[`IDX] | inst_ad_nxt[`SYMB] | inst_ad_nxt[`ABS]; +wire dst_acalc = inst_ad[`IDX] | inst_ad[`SYMB] | inst_ad[`ABS]; +wire dst_rd_pre = inst_ad_nxt[`IDX] | inst_so_nxt[`PUSH] | inst_so_nxt[`CALL] | inst_so_nxt[`RETI]; +wire dst_rd = inst_ad[`IDX] | inst_so[`PUSH] | inst_so[`CALL] | inst_so[`RETI]; + +wire inst_branch = (inst_ad_nxt[`DIR] & (ir[3:0]==4'h0)) | inst_type_nxt[`INST_JMP] | inst_so_nxt[`RETI]; + +reg exec_jmp; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) exec_jmp <= 1'b0; + else if (inst_branch & decode) exec_jmp <= 1'b1; + else if (e_state==E_JUMP) exec_jmp <= 1'b0; + +reg exec_dst_wr; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) exec_dst_wr <= 1'b0; + else if (e_state==E_DST_RD) exec_dst_wr <= 1'b1; + else if (e_state==E_DST_WR) exec_dst_wr <= 1'b0; + +reg exec_src_wr; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) exec_src_wr <= 1'b0; + else if (inst_type[`INST_SO] & (e_state==E_SRC_RD)) exec_src_wr <= 1'b1; + else if ((e_state==E_SRC_WR) || (e_state==E_DST_WR)) exec_src_wr <= 1'b0; + +reg exec_dext_rdy; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) exec_dext_rdy <= 1'b0; + else if (e_state==E_DST_RD) exec_dext_rdy <= 1'b0; + else if (inst_dext_rdy) exec_dext_rdy <= 1'b1; + +// Execution first state +wire [3:0] e_first_state = ~dbg_halt_st & inst_so_nxt[`IRQ] ? E_IRQ_0 : + cpu_halt_cmd | (i_state==I_IDLE) ? E_IDLE : + cpuoff ? E_IDLE : + src_acalc_pre ? E_SRC_AD : + src_rd_pre ? E_SRC_RD : + dst_acalc_pre ? E_DST_AD : + dst_rd_pre ? E_DST_RD : E_EXEC; + + +// State machine +//-------------------------------- + +// States Transitions +always @(e_state or dst_acalc or dst_rd or inst_sext_rdy or + inst_dext_rdy or exec_dext_rdy or exec_jmp or exec_dst_wr or + e_first_state or exec_src_wr) + case(e_state) + E_IDLE : e_state_nxt = e_first_state; + E_IRQ_0 : e_state_nxt = E_IRQ_1; + E_IRQ_1 : e_state_nxt = E_IRQ_2; + E_IRQ_2 : e_state_nxt = E_IRQ_3; + E_IRQ_3 : e_state_nxt = E_IRQ_4; + E_IRQ_4 : e_state_nxt = E_EXEC; + + E_SRC_AD : e_state_nxt = inst_sext_rdy ? E_SRC_RD : E_SRC_AD; + + E_SRC_RD : e_state_nxt = dst_acalc ? E_DST_AD : + dst_rd ? E_DST_RD : E_EXEC; + + E_DST_AD : e_state_nxt = (inst_dext_rdy | + exec_dext_rdy) ? E_DST_RD : E_DST_AD; + + E_DST_RD : e_state_nxt = E_EXEC; + + E_EXEC : e_state_nxt = exec_dst_wr ? E_DST_WR : + exec_jmp ? E_JUMP : + exec_src_wr ? E_SRC_WR : e_first_state; + + E_JUMP : e_state_nxt = e_first_state; + E_DST_WR : e_state_nxt = exec_jmp ? E_JUMP : e_first_state; + E_SRC_WR : e_state_nxt = e_first_state; + // pragma coverage off + default : e_state_nxt = E_IRQ_0; + // pragma coverage on + endcase + +// State machine +always @(posedge mclk or posedge puc_rst) + if (puc_rst) e_state <= E_IRQ_1; + else e_state <= e_state_nxt; + + +// Frontend State machine control signals +//---------------------------------------- + +wire exec_done = exec_jmp ? (e_state==E_JUMP) : + exec_dst_wr ? (e_state==E_DST_WR) : + exec_src_wr ? (e_state==E_SRC_WR) : (e_state==E_EXEC); + + +//============================================================================= +// 8) EXECUTION-UNIT STATE CONTROL +//============================================================================= + +// +// 8.1) ALU CONTROL SIGNALS +//------------------------------------- +// +// 12'b000000000001: Enable ALU source inverter +// 12'b000000000010: Enable Incrementer +// 12'b000000000100: Enable Incrementer on carry bit +// 12'b000000001000: Select Adder +// 12'b000000010000: Select AND +// 12'b000000100000: Select OR +// 12'b000001000000: Select XOR +// 12'b000010000000: Select DADD +// 12'b000100000000: Update N, Z & C (C=~Z) +// 12'b001000000000: Update all status bits +// 12'b010000000000: Update status bit for XOR instruction +// 12'b100000000000: Don't write to destination + +reg [11:0] inst_alu; + +wire alu_src_inv = inst_to_nxt[`SUB] | inst_to_nxt[`SUBC] | + inst_to_nxt[`CMP] | inst_to_nxt[`BIC] ; + +wire alu_inc = inst_to_nxt[`SUB] | inst_to_nxt[`CMP]; + +wire alu_inc_c = inst_to_nxt[`ADDC] | inst_to_nxt[`DADD] | + inst_to_nxt[`SUBC]; + +wire alu_add = inst_to_nxt[`ADD] | inst_to_nxt[`ADDC] | + inst_to_nxt[`SUB] | inst_to_nxt[`SUBC] | + inst_to_nxt[`CMP] | inst_type_nxt[`INST_JMP] | + inst_so_nxt[`RETI]; + + +wire alu_and = inst_to_nxt[`AND] | inst_to_nxt[`BIC] | + inst_to_nxt[`BIT]; + +wire alu_or = inst_to_nxt[`BIS]; + +wire alu_xor = inst_to_nxt[`XOR]; + +wire alu_dadd = inst_to_nxt[`DADD]; + +wire alu_stat_7 = inst_to_nxt[`BIT] | inst_to_nxt[`AND] | + inst_so_nxt[`SXT]; + +wire alu_stat_f = inst_to_nxt[`ADD] | inst_to_nxt[`ADDC] | + inst_to_nxt[`SUB] | inst_to_nxt[`SUBC] | + inst_to_nxt[`CMP] | inst_to_nxt[`DADD] | + inst_to_nxt[`BIT] | inst_to_nxt[`XOR] | + inst_to_nxt[`AND] | + inst_so_nxt[`RRC] | inst_so_nxt[`RRA] | + inst_so_nxt[`SXT]; + +wire alu_shift = inst_so_nxt[`RRC] | inst_so_nxt[`RRA]; + +wire exec_no_wr = inst_to_nxt[`CMP] | inst_to_nxt[`BIT]; + +wire [11:0] inst_alu_nxt = {exec_no_wr, + alu_shift, + alu_stat_f, + alu_stat_7, + alu_dadd, + alu_xor, + alu_or, + alu_and, + alu_add, + alu_inc_c, + alu_inc, + alu_src_inv}; + +always @(posedge mclk_decode or posedge puc_rst) + if (puc_rst) inst_alu <= 12'h000; +`ifdef CLOCK_GATING + else inst_alu <= inst_alu_nxt; +`else + else if (decode) inst_alu <= inst_alu_nxt; +`endif + + +endmodule // omsp_frontend + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_mem_backbone.v b/tests/openmsp430/rtl/omsp_mem_backbone.v new file mode 100644 index 000000000..299cbff88 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_mem_backbone.v @@ -0,0 +1,275 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_mem_backbone.v +// +// *Module Description: +// Memory interface backbone (decoder + arbiter) +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 151 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-07-23 00:24:11 +0200 (Mon, 23 Jul 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_mem_backbone ( + +// OUTPUTs + dbg_mem_din, // Debug unit Memory data input + dmem_addr, // Data Memory address + dmem_cen, // Data Memory chip enable (low active) + dmem_din, // Data Memory data input + dmem_wen, // Data Memory write enable (low active) + eu_mdb_in, // Execution Unit Memory data bus input + fe_mdb_in, // Frontend Memory data bus input + fe_pmem_wait, // Frontend wait for Instruction fetch + per_addr, // Peripheral address + per_din, // Peripheral data input + per_we, // Peripheral write enable (high active) + per_en, // Peripheral enable (high active) + pmem_addr, // Program Memory address + pmem_cen, // Program Memory chip enable (low active) + pmem_din, // Program Memory data input (optional) + pmem_wen, // Program Memory write enable (low active) (optional) + +// INPUTs + dbg_halt_st, // Halt/Run status from CPU + dbg_mem_addr, // Debug address for rd/wr access + dbg_mem_dout, // Debug unit data output + dbg_mem_en, // Debug unit memory enable + dbg_mem_wr, // Debug unit memory write + dmem_dout, // Data Memory data output + eu_mab, // Execution Unit Memory address bus + eu_mb_en, // Execution Unit Memory bus enable + eu_mb_wr, // Execution Unit Memory bus write transfer + eu_mdb_out, // Execution Unit Memory data bus output + fe_mab, // Frontend Memory address bus + fe_mb_en, // Frontend Memory bus enable + mclk, // Main system clock + per_dout, // Peripheral data output + pmem_dout, // Program Memory data output + puc_rst, // Main system reset + scan_enable // Scan enable (active during scan shifting) +); + +// OUTPUTs +//========= +output [15:0] dbg_mem_din; // Debug unit Memory data input +output [`DMEM_MSB:0] dmem_addr; // Data Memory address +output dmem_cen; // Data Memory chip enable (low active) +output [15:0] dmem_din; // Data Memory data input +output [1:0] dmem_wen; // Data Memory write enable (low active) +output [15:0] eu_mdb_in; // Execution Unit Memory data bus input +output [15:0] fe_mdb_in; // Frontend Memory data bus input +output fe_pmem_wait; // Frontend wait for Instruction fetch +output [13:0] per_addr; // Peripheral address +output [15:0] per_din; // Peripheral data input +output [1:0] per_we; // Peripheral write enable (high active) +output per_en; // Peripheral enable (high active) +output [`PMEM_MSB:0] pmem_addr; // Program Memory address +output pmem_cen; // Program Memory chip enable (low active) +output [15:0] pmem_din; // Program Memory data input (optional) +output [1:0] pmem_wen; // Program Memory write enable (low active) (optional) + +// INPUTs +//========= +input dbg_halt_st; // Halt/Run status from CPU +input [15:0] dbg_mem_addr; // Debug address for rd/wr access +input [15:0] dbg_mem_dout; // Debug unit data output +input dbg_mem_en; // Debug unit memory enable +input [1:0] dbg_mem_wr; // Debug unit memory write +input [15:0] dmem_dout; // Data Memory data output +input [14:0] eu_mab; // Execution Unit Memory address bus +input eu_mb_en; // Execution Unit Memory bus enable +input [1:0] eu_mb_wr; // Execution Unit Memory bus write transfer +input [15:0] eu_mdb_out; // Execution Unit Memory data bus output +input [14:0] fe_mab; // Frontend Memory address bus +input fe_mb_en; // Frontend Memory bus enable +input mclk; // Main system clock +input [15:0] per_dout; // Peripheral data output +input [15:0] pmem_dout; // Program Memory data output +input puc_rst; // Main system reset +input scan_enable; // Scan enable (active during scan shifting) + + +//============================================================================= +// 1) DECODER +//============================================================================= + +// RAM Interface +//------------------ + +// Execution unit access +wire eu_dmem_cen = ~(eu_mb_en & (eu_mab>=(`DMEM_BASE>>1)) & + (eu_mab<((`DMEM_BASE+`DMEM_SIZE)>>1))); +wire [15:0] eu_dmem_addr = {1'b0, eu_mab}-(`DMEM_BASE>>1); + +// Debug interface access +wire dbg_dmem_cen = ~(dbg_mem_en & (dbg_mem_addr[15:1]>=(`DMEM_BASE>>1)) & + (dbg_mem_addr[15:1]<((`DMEM_BASE+`DMEM_SIZE)>>1))); +wire [15:0] dbg_dmem_addr = {1'b0, dbg_mem_addr[15:1]}-(`DMEM_BASE>>1); + + +// RAM Interface +wire [`DMEM_MSB:0] dmem_addr = ~dbg_dmem_cen ? dbg_dmem_addr[`DMEM_MSB:0] : eu_dmem_addr[`DMEM_MSB:0]; +wire dmem_cen = dbg_dmem_cen & eu_dmem_cen; +wire [1:0] dmem_wen = ~(dbg_mem_wr | eu_mb_wr); +wire [15:0] dmem_din = ~dbg_dmem_cen ? dbg_mem_dout : eu_mdb_out; + + +// ROM Interface +//------------------ +parameter PMEM_OFFSET = (16'hFFFF-`PMEM_SIZE+1); + +// Execution unit access (only read access are accepted) +wire eu_pmem_cen = ~(eu_mb_en & ~|eu_mb_wr & (eu_mab>=(PMEM_OFFSET>>1))); +wire [15:0] eu_pmem_addr = eu_mab-(PMEM_OFFSET>>1); + +// Front-end access +wire fe_pmem_cen = ~(fe_mb_en & (fe_mab>=(PMEM_OFFSET>>1))); +wire [15:0] fe_pmem_addr = fe_mab-(PMEM_OFFSET>>1); + +// Debug interface access +wire dbg_pmem_cen = ~(dbg_mem_en & (dbg_mem_addr[15:1]>=(PMEM_OFFSET>>1))); +wire [15:0] dbg_pmem_addr = {1'b0, dbg_mem_addr[15:1]}-(PMEM_OFFSET>>1); + + +// ROM Interface (Execution unit has priority) +wire [`PMEM_MSB:0] pmem_addr = ~dbg_pmem_cen ? dbg_pmem_addr[`PMEM_MSB:0] : + ~eu_pmem_cen ? eu_pmem_addr[`PMEM_MSB:0] : fe_pmem_addr[`PMEM_MSB:0]; +wire pmem_cen = fe_pmem_cen & eu_pmem_cen & dbg_pmem_cen; +wire [1:0] pmem_wen = ~dbg_mem_wr; +wire [15:0] pmem_din = dbg_mem_dout; + +wire fe_pmem_wait = (~fe_pmem_cen & ~eu_pmem_cen); + + +// Peripherals +//-------------------- +wire dbg_per_en = dbg_mem_en & (dbg_mem_addr[15:1]<(`PER_SIZE>>1)); +wire eu_per_en = eu_mb_en & (eu_mab<(`PER_SIZE>>1)); + +wire [15:0] per_din = dbg_mem_en ? dbg_mem_dout : eu_mdb_out; +wire [1:0] per_we = dbg_mem_en ? dbg_mem_wr : eu_mb_wr; +wire per_en = dbg_mem_en ? dbg_per_en : eu_per_en; +wire [`PER_MSB:0] per_addr_mux = dbg_mem_en ? dbg_mem_addr[`PER_MSB+1:1] : eu_mab[`PER_MSB:0]; +wire [14:0] per_addr_ful = {{15-`PER_AWIDTH{1'b0}}, per_addr_mux}; +wire [13:0] per_addr = per_addr_ful[13:0]; + +reg [15:0] per_dout_val; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) per_dout_val <= 16'h0000; + else per_dout_val <= per_dout; + + +// Frontend data Mux +//--------------------------------- +// Whenever the frontend doesn't access the ROM, backup the data + +// Detect whenever the data should be backuped and restored +reg fe_pmem_cen_dly; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) fe_pmem_cen_dly <= 1'b0; + else fe_pmem_cen_dly <= fe_pmem_cen; + +wire fe_pmem_save = ( fe_pmem_cen & ~fe_pmem_cen_dly) & ~dbg_halt_st; +wire fe_pmem_restore = (~fe_pmem_cen & fe_pmem_cen_dly) | dbg_halt_st; + +`ifdef CLOCK_GATING +wire mclk_bckup; +omsp_clock_gate clock_gate_bckup (.gclk(mclk_bckup), + .clk (mclk), .enable(fe_pmem_save), .scan_enable(scan_enable)); +`else +wire mclk_bckup = mclk; +`endif + +reg [15:0] pmem_dout_bckup; +always @(posedge mclk_bckup or posedge puc_rst) + if (puc_rst) pmem_dout_bckup <= 16'h0000; +`ifdef CLOCK_GATING + else pmem_dout_bckup <= pmem_dout; +`else + else if (fe_pmem_save) pmem_dout_bckup <= pmem_dout; +`endif + +// Mux between the ROM data and the backup +reg pmem_dout_bckup_sel; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) pmem_dout_bckup_sel <= 1'b0; + else if (fe_pmem_save) pmem_dout_bckup_sel <= 1'b1; + else if (fe_pmem_restore) pmem_dout_bckup_sel <= 1'b0; + +assign fe_mdb_in = pmem_dout_bckup_sel ? pmem_dout_bckup : pmem_dout; + + +// Execution-Unit data Mux +//--------------------------------- + +// Select between peripherals, RAM and ROM +reg [1:0] eu_mdb_in_sel; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) eu_mdb_in_sel <= 2'b00; + else eu_mdb_in_sel <= {~eu_pmem_cen, per_en}; + +// Mux +assign eu_mdb_in = eu_mdb_in_sel[1] ? pmem_dout : + eu_mdb_in_sel[0] ? per_dout_val : dmem_dout; + +// Debug interface data Mux +//--------------------------------- + +// Select between peripherals, RAM and ROM +`ifdef DBG_EN +reg [1:0] dbg_mem_din_sel; +always @(posedge mclk or posedge puc_rst) + if (puc_rst) dbg_mem_din_sel <= 2'b00; + else dbg_mem_din_sel <= {~dbg_pmem_cen, dbg_per_en}; + +`else +wire [1:0] dbg_mem_din_sel = 2'b00; +`endif + +// Mux +assign dbg_mem_din = dbg_mem_din_sel[1] ? pmem_dout : + dbg_mem_din_sel[0] ? per_dout_val : dmem_dout; + + +endmodule // omsp_mem_backbone + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_multiplier.v b/tests/openmsp430/rtl/omsp_multiplier.v new file mode 100644 index 000000000..4f7b04cad --- /dev/null +++ b/tests/openmsp430/rtl/omsp_multiplier.v @@ -0,0 +1,420 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_multiplier.v +// +// *Module Description: +// 16x16 Hardware multiplier. +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 23 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2009-08-30 18:39:26 +0200 (Sun, 30 Aug 2009) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_multiplier ( + +// OUTPUTs + per_dout, // Peripheral data output + +// INPUTs + mclk, // Main system clock + per_addr, // Peripheral address + per_din, // Peripheral data input + per_en, // Peripheral enable (high active) + per_we, // Peripheral write enable (high active) + puc_rst, // Main system reset + scan_enable // Scan enable (active during scan shifting) +); + +// OUTPUTs +//========= +output [15:0] per_dout; // Peripheral data output + +// INPUTs +//========= +input mclk; // Main system clock +input [13:0] per_addr; // Peripheral address +input [15:0] per_din; // Peripheral data input +input per_en; // Peripheral enable (high active) +input [1:0] per_we; // Peripheral write enable (high active) +input puc_rst; // Main system reset +input scan_enable; // Scan enable (active during scan shifting) + + +//============================================================================= +// 1) PARAMETER/REGISTERS & WIRE DECLARATION +//============================================================================= + +// Register base address (must be aligned to decoder bit width) +parameter [14:0] BASE_ADDR = 15'h0130; + +// Decoder bit width (defines how many bits are considered for address decoding) +parameter DEC_WD = 4; + +// Register addresses offset +parameter [DEC_WD-1:0] OP1_MPY = 'h0, + OP1_MPYS = 'h2, + OP1_MAC = 'h4, + OP1_MACS = 'h6, + OP2 = 'h8, + RESLO = 'hA, + RESHI = 'hC, + SUMEXT = 'hE; + +// Register one-hot decoder utilities +parameter DEC_SZ = (1 << DEC_WD); +parameter [DEC_SZ-1:0] BASE_REG = {{DEC_SZ-1{1'b0}}, 1'b1}; + +// Register one-hot decoder +parameter [DEC_SZ-1:0] OP1_MPY_D = (BASE_REG << OP1_MPY), + OP1_MPYS_D = (BASE_REG << OP1_MPYS), + OP1_MAC_D = (BASE_REG << OP1_MAC), + OP1_MACS_D = (BASE_REG << OP1_MACS), + OP2_D = (BASE_REG << OP2), + RESLO_D = (BASE_REG << RESLO), + RESHI_D = (BASE_REG << RESHI), + SUMEXT_D = (BASE_REG << SUMEXT); + + +// Wire pre-declarations +wire result_wr; +wire result_clr; +wire early_read; + + +//============================================================================ +// 2) REGISTER DECODER +//============================================================================ + +// Local register selection +wire reg_sel = per_en & (per_addr[13:DEC_WD-1]==BASE_ADDR[14:DEC_WD]); + +// Register local address +wire [DEC_WD-1:0] reg_addr = {per_addr[DEC_WD-2:0], 1'b0}; + +// Register address decode +wire [DEC_SZ-1:0] reg_dec = (OP1_MPY_D & {DEC_SZ{(reg_addr == OP1_MPY )}}) | + (OP1_MPYS_D & {DEC_SZ{(reg_addr == OP1_MPYS )}}) | + (OP1_MAC_D & {DEC_SZ{(reg_addr == OP1_MAC )}}) | + (OP1_MACS_D & {DEC_SZ{(reg_addr == OP1_MACS )}}) | + (OP2_D & {DEC_SZ{(reg_addr == OP2 )}}) | + (RESLO_D & {DEC_SZ{(reg_addr == RESLO )}}) | + (RESHI_D & {DEC_SZ{(reg_addr == RESHI )}}) | + (SUMEXT_D & {DEC_SZ{(reg_addr == SUMEXT )}}); + +// Read/Write probes +wire reg_write = |per_we & reg_sel; +wire reg_read = ~|per_we & reg_sel; + +// Read/Write vectors +wire [DEC_SZ-1:0] reg_wr = reg_dec & {DEC_SZ{reg_write}}; +wire [DEC_SZ-1:0] reg_rd = reg_dec & {DEC_SZ{reg_read}}; + + +//============================================================================ +// 3) REGISTERS +//============================================================================ + +// OP1 Register +//----------------- +reg [15:0] op1; + +wire op1_wr = reg_wr[OP1_MPY] | + reg_wr[OP1_MPYS] | + reg_wr[OP1_MAC] | + reg_wr[OP1_MACS]; + +`ifdef CLOCK_GATING +wire mclk_op1; +omsp_clock_gate clock_gate_op1 (.gclk(mclk_op1), + .clk (mclk), .enable(op1_wr), .scan_enable(scan_enable)); +`else +wire mclk_op1 = mclk; +`endif + +always @ (posedge mclk_op1 or posedge puc_rst) + if (puc_rst) op1 <= 16'h0000; +`ifdef CLOCK_GATING + else op1 <= per_din; +`else + else if (op1_wr) op1 <= per_din; +`endif + +wire [15:0] op1_rd = op1; + + +// OP2 Register +//----------------- +reg [15:0] op2; + +wire op2_wr = reg_wr[OP2]; + +`ifdef CLOCK_GATING +wire mclk_op2; +omsp_clock_gate clock_gate_op2 (.gclk(mclk_op2), + .clk (mclk), .enable(op2_wr), .scan_enable(scan_enable)); +`else +wire mclk_op2 = mclk; +`endif + +always @ (posedge mclk_op2 or posedge puc_rst) + if (puc_rst) op2 <= 16'h0000; +`ifdef CLOCK_GATING + else op2 <= per_din; +`else + else if (op2_wr) op2 <= per_din; +`endif + +wire [15:0] op2_rd = op2; + + +// RESLO Register +//----------------- +reg [15:0] reslo; + +wire [15:0] reslo_nxt; +wire reslo_wr = reg_wr[RESLO]; + +`ifdef CLOCK_GATING +wire reslo_en = reslo_wr | result_clr | result_wr; +wire mclk_reslo; +omsp_clock_gate clock_gate_reslo (.gclk(mclk_reslo), + .clk (mclk), .enable(reslo_en), .scan_enable(scan_enable)); +`else +wire mclk_reslo = mclk; +`endif + +always @ (posedge mclk_reslo or posedge puc_rst) + if (puc_rst) reslo <= 16'h0000; + else if (reslo_wr) reslo <= per_din; + else if (result_clr) reslo <= 16'h0000; +`ifdef CLOCK_GATING + else reslo <= reslo_nxt; +`else + else if (result_wr) reslo <= reslo_nxt; +`endif + +wire [15:0] reslo_rd = early_read ? reslo_nxt : reslo; + + +// RESHI Register +//----------------- +reg [15:0] reshi; + +wire [15:0] reshi_nxt; +wire reshi_wr = reg_wr[RESHI]; + +`ifdef CLOCK_GATING +wire reshi_en = reshi_wr | result_clr | result_wr; +wire mclk_reshi; +omsp_clock_gate clock_gate_reshi (.gclk(mclk_reshi), + .clk (mclk), .enable(reshi_en), .scan_enable(scan_enable)); +`else +wire mclk_reshi = mclk; +`endif + +always @ (posedge mclk_reshi or posedge puc_rst) + if (puc_rst) reshi <= 16'h0000; + else if (reshi_wr) reshi <= per_din; + else if (result_clr) reshi <= 16'h0000; +`ifdef CLOCK_GATING + else reshi <= reshi_nxt; +`else + else if (result_wr) reshi <= reshi_nxt; +`endif + +wire [15:0] reshi_rd = early_read ? reshi_nxt : reshi; + + +// SUMEXT Register +//----------------- +reg [1:0] sumext_s; + +wire [1:0] sumext_s_nxt; + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) sumext_s <= 2'b00; + else if (op2_wr) sumext_s <= 2'b00; + else if (result_wr) sumext_s <= sumext_s_nxt; + +wire [15:0] sumext_nxt = {{14{sumext_s_nxt[1]}}, sumext_s_nxt}; +wire [15:0] sumext = {{14{sumext_s[1]}}, sumext_s}; +wire [15:0] sumext_rd = early_read ? sumext_nxt : sumext; + + +//============================================================================ +// 4) DATA OUTPUT GENERATION +//============================================================================ + +// Data output mux +wire [15:0] op1_mux = op1_rd & {16{reg_rd[OP1_MPY] | + reg_rd[OP1_MPYS] | + reg_rd[OP1_MAC] | + reg_rd[OP1_MACS]}}; +wire [15:0] op2_mux = op2_rd & {16{reg_rd[OP2]}}; +wire [15:0] reslo_mux = reslo_rd & {16{reg_rd[RESLO]}}; +wire [15:0] reshi_mux = reshi_rd & {16{reg_rd[RESHI]}}; +wire [15:0] sumext_mux = sumext_rd & {16{reg_rd[SUMEXT]}}; + +wire [15:0] per_dout = op1_mux | + op2_mux | + reslo_mux | + reshi_mux | + sumext_mux; + + +//============================================================================ +// 5) HARDWARE MULTIPLIER FUNCTIONAL LOGIC +//============================================================================ + +// Multiplier configuration +//-------------------------- + +// Detect signed mode +reg sign_sel; +always @ (posedge mclk_op1 or posedge puc_rst) + if (puc_rst) sign_sel <= 1'b0; +`ifdef CLOCK_GATING + else sign_sel <= reg_wr[OP1_MPYS] | reg_wr[OP1_MACS]; +`else + else if (op1_wr) sign_sel <= reg_wr[OP1_MPYS] | reg_wr[OP1_MACS]; +`endif + + +// Detect accumulate mode +reg acc_sel; +always @ (posedge mclk_op1 or posedge puc_rst) + if (puc_rst) acc_sel <= 1'b0; +`ifdef CLOCK_GATING + else acc_sel <= reg_wr[OP1_MAC] | reg_wr[OP1_MACS]; +`else + else if (op1_wr) acc_sel <= reg_wr[OP1_MAC] | reg_wr[OP1_MACS]; +`endif + + +// Detect whenever the RESHI and RESLO registers should be cleared +assign result_clr = op2_wr & ~acc_sel; + +// Combine RESHI & RESLO +wire [31:0] result = {reshi, reslo}; + + +// 16x16 Multiplier (result computed in 1 clock cycle) +//----------------------------------------------------- +`ifdef MPY_16x16 + +// Detect start of a multiplication +reg cycle; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) cycle <= 1'b0; + else cycle <= op2_wr; + +assign result_wr = cycle; + +// Expand the operands to support signed & unsigned operations +wire signed [16:0] op1_xp = {sign_sel & op1[15], op1}; +wire signed [16:0] op2_xp = {sign_sel & op2[15], op2}; + + +// 17x17 signed multiplication +wire signed [33:0] product = op1_xp * op2_xp; + +// Accumulate +wire [32:0] result_nxt = {1'b0, result} + {1'b0, product[31:0]}; + + +// Next register values +assign reslo_nxt = result_nxt[15:0]; +assign reshi_nxt = result_nxt[31:16]; +assign sumext_s_nxt = sign_sel ? {2{result_nxt[31]}} : + {1'b0, result_nxt[32]}; + + +// Since the MAC is completed within 1 clock cycle, +// an early read can't happen. +assign early_read = 1'b0; + + +// 16x8 Multiplier (result computed in 2 clock cycles) +//----------------------------------------------------- +`else + +// Detect start of a multiplication +reg [1:0] cycle; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) cycle <= 2'b00; + else cycle <= {cycle[0], op2_wr}; + +assign result_wr = |cycle; + + +// Expand the operands to support signed & unsigned operations +wire signed [16:0] op1_xp = {sign_sel & op1[15], op1}; +wire signed [8:0] op2_hi_xp = {sign_sel & op2[15], op2[15:8]}; +wire signed [8:0] op2_lo_xp = { 1'b0, op2[7:0]}; +wire signed [8:0] op2_xp = cycle[0] ? op2_hi_xp : op2_lo_xp; + + +// 17x9 signed multiplication +wire signed [25:0] product = op1_xp * op2_xp; + +wire [31:0] product_xp = cycle[0] ? {product[23:0], 8'h00} : + {{8{sign_sel & product[23]}}, product[23:0]}; + +// Accumulate +wire [32:0] result_nxt = {1'b0, result} + {1'b0, product_xp[31:0]}; + + +// Next register values +assign reslo_nxt = result_nxt[15:0]; +assign reshi_nxt = result_nxt[31:16]; +assign sumext_s_nxt = sign_sel ? {2{result_nxt[31]}} : + {1'b0, result_nxt[32] | sumext_s[0]}; + +// Since the MAC is completed within 2 clock cycle, +// an early read can happen during the second cycle. +assign early_read = cycle[1]; + +`endif + + +endmodule // omsp_multiplier + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_register_file.v b/tests/openmsp430/rtl/omsp_register_file.v new file mode 100644 index 000000000..2ccd24991 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_register_file.v @@ -0,0 +1,618 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_register_file.v +// +// *Module Description: +// openMSP430 Register files +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_register_file ( + +// OUTPUTs + cpuoff, // Turns off the CPU + gie, // General interrupt enable + oscoff, // Turns off LFXT1 clock input + pc_sw, // Program counter software value + pc_sw_wr, // Program counter software write + reg_dest, // Selected register destination content + reg_src, // Selected register source content + scg0, // System clock generator 1. Turns off the DCO + scg1, // System clock generator 1. Turns off the SMCLK + status, // R2 Status {V,N,Z,C} + +// INPUTs + alu_stat, // ALU Status {V,N,Z,C} + alu_stat_wr, // ALU Status write {V,N,Z,C} + inst_bw, // Decoded Inst: byte width + inst_dest, // Register destination selection + inst_src, // Register source selection + mclk, // Main system clock + pc, // Program counter + puc_rst, // Main system reset + reg_dest_val, // Selected register destination value + reg_dest_wr, // Write selected register destination + reg_pc_call, // Trigger PC update for a CALL instruction + reg_sp_val, // Stack Pointer next value + reg_sp_wr, // Stack Pointer write + reg_sr_wr, // Status register update for RETI instruction + reg_sr_clr, // Status register clear for interrupts + reg_incr, // Increment source register + scan_enable // Scan enable (active during scan shifting) +); + +// OUTPUTs +//========= +output cpuoff; // Turns off the CPU +output gie; // General interrupt enable +output oscoff; // Turns off LFXT1 clock input +output [15:0] pc_sw; // Program counter software value +output pc_sw_wr; // Program counter software write +output [15:0] reg_dest; // Selected register destination content +output [15:0] reg_src; // Selected register source content +output scg0; // System clock generator 1. Turns off the DCO +output scg1; // System clock generator 1. Turns off the SMCLK +output [3:0] status; // R2 Status {V,N,Z,C} + +// INPUTs +//========= +input [3:0] alu_stat; // ALU Status {V,N,Z,C} +input [3:0] alu_stat_wr; // ALU Status write {V,N,Z,C} +input inst_bw; // Decoded Inst: byte width +input [15:0] inst_dest; // Register destination selection +input [15:0] inst_src; // Register source selection +input mclk; // Main system clock +input [15:0] pc; // Program counter +input puc_rst; // Main system reset +input [15:0] reg_dest_val; // Selected register destination value +input reg_dest_wr; // Write selected register destination +input reg_pc_call; // Trigger PC update for a CALL instruction +input [15:0] reg_sp_val; // Stack Pointer next value +input reg_sp_wr; // Stack Pointer write +input reg_sr_wr; // Status register update for RETI instruction +input reg_sr_clr; // Status register clear for interrupts +input reg_incr; // Increment source register +input scan_enable; // Scan enable (active during scan shifting) + + +//============================================================================= +// 1) AUTOINCREMENT UNIT +//============================================================================= + +wire [15:0] inst_src_in; +wire [15:0] incr_op = (inst_bw & ~inst_src_in[1]) ? 16'h0001 : 16'h0002; +wire [15:0] reg_incr_val = reg_src+incr_op; + +wire [15:0] reg_dest_val_in = inst_bw ? {8'h00,reg_dest_val[7:0]} : reg_dest_val; + + +//============================================================================= +// 2) SPECIAL REGISTERS (R1/R2/R3) +//============================================================================= + +// Source input selection mask (for interrupt support) +//----------------------------------------------------- + +assign inst_src_in = reg_sr_clr ? 16'h0004 : inst_src; + + +// R0: Program counter +//--------------------- + +wire [15:0] r0 = pc; + +wire [15:0] pc_sw = reg_dest_val_in; +wire pc_sw_wr = (inst_dest[0] & reg_dest_wr) | reg_pc_call; + + +// R1: Stack pointer +//------------------- +reg [15:0] r1; +wire r1_wr = inst_dest[1] & reg_dest_wr; +wire r1_inc = inst_src_in[1] & reg_incr; + +`ifdef CLOCK_GATING +wire r1_en = r1_wr | reg_sp_wr | r1_inc; +wire mclk_r1; +omsp_clock_gate clock_gate_r1 (.gclk(mclk_r1), + .clk (mclk), .enable(r1_en), .scan_enable(scan_enable)); +`else +wire mclk_r1 = mclk; +`endif + +always @(posedge mclk_r1 or posedge puc_rst) + if (puc_rst) r1 <= 16'h0000; + else if (r1_wr) r1 <= reg_dest_val_in & 16'hfffe; + else if (reg_sp_wr) r1 <= reg_sp_val & 16'hfffe; +`ifdef CLOCK_GATING + else r1 <= reg_incr_val & 16'hfffe; +`else + else if (r1_inc) r1 <= reg_incr_val & 16'hfffe; +`endif + + +// R2: Status register +//--------------------- +reg [15:0] r2; +wire r2_wr = (inst_dest[2] & reg_dest_wr) | reg_sr_wr; + +`ifdef CLOCK_GATING // -- WITH CLOCK GATING -- +wire r2_c = alu_stat_wr[0] ? alu_stat[0] : reg_dest_val_in[0]; // C + +wire r2_z = alu_stat_wr[1] ? alu_stat[1] : reg_dest_val_in[1]; // Z + +wire r2_n = alu_stat_wr[2] ? alu_stat[2] : reg_dest_val_in[2]; // N + +wire [7:3] r2_nxt = r2_wr ? reg_dest_val_in[7:3] : r2[7:3]; + +wire r2_v = alu_stat_wr[3] ? alu_stat[3] : reg_dest_val_in[8]; // V + +wire r2_en = |alu_stat_wr | r2_wr | reg_sr_clr; +wire mclk_r2; +omsp_clock_gate clock_gate_r2 (.gclk(mclk_r2), + .clk (mclk), .enable(r2_en), .scan_enable(scan_enable)); + +`else // -- WITHOUT CLOCK GATING -- +wire r2_c = alu_stat_wr[0] ? alu_stat[0] : + r2_wr ? reg_dest_val_in[0] : r2[0]; // C + +wire r2_z = alu_stat_wr[1] ? alu_stat[1] : + r2_wr ? reg_dest_val_in[1] : r2[1]; // Z + +wire r2_n = alu_stat_wr[2] ? alu_stat[2] : + r2_wr ? reg_dest_val_in[2] : r2[2]; // N + +wire [7:3] r2_nxt = r2_wr ? reg_dest_val_in[7:3] : r2[7:3]; + +wire r2_v = alu_stat_wr[3] ? alu_stat[3] : + r2_wr ? reg_dest_val_in[8] : r2[8]; // V + + +wire mclk_r2 = mclk; +`endif + +`ifdef ASIC + `ifdef CPUOFF_EN + wire [15:0] cpuoff_mask = 16'h0010; + `else + wire [15:0] cpuoff_mask = 16'h0000; + `endif + `ifdef OSCOFF_EN + wire [15:0] oscoff_mask = 16'h0020; + `else + wire [15:0] oscoff_mask = 16'h0000; + `endif + `ifdef SCG0_EN + wire [15:0] scg0_mask = 16'h0040; + `else + wire [15:0] scg0_mask = 16'h0000; + `endif + `ifdef SCG1_EN + wire [15:0] scg1_mask = 16'h0080; + `else + wire [15:0] scg1_mask = 16'h0000; + `endif +`else + wire [15:0] cpuoff_mask = 16'h0010; // For the FPGA version: - the CPUOFF mode is emulated + wire [15:0] oscoff_mask = 16'h0020; // - the SCG1 mode is emulated + wire [15:0] scg0_mask = 16'h0000; // - the SCG0 is not supported + wire [15:0] scg1_mask = 16'h0080; // - the SCG1 mode is emulated +`endif + + wire [15:0] r2_mask = cpuoff_mask | oscoff_mask | scg0_mask | scg1_mask | 16'h010f; + +always @(posedge mclk_r2 or posedge puc_rst) + if (puc_rst) r2 <= 16'h0000; + else if (reg_sr_clr) r2 <= 16'h0000; + else r2 <= {7'h00, r2_v, r2_nxt, r2_n, r2_z, r2_c} & r2_mask; + +assign status = {r2[8], r2[2:0]}; +assign gie = r2[3]; +assign cpuoff = r2[4] | (r2_nxt[4] & r2_wr & cpuoff_mask[4]); +assign oscoff = r2[5]; +assign scg0 = r2[6]; +assign scg1 = r2[7]; + + +// R3: Constant generator +//------------------------------------------------------------- +// Note: the auto-increment feature is not implemented for R3 +// because the @R3+ addressing mode is used for constant +// generation (#-1). +reg [15:0] r3; +wire r3_wr = inst_dest[3] & reg_dest_wr; + +`ifdef CLOCK_GATING +wire r3_en = r3_wr; +wire mclk_r3; +omsp_clock_gate clock_gate_r3 (.gclk(mclk_r3), + .clk (mclk), .enable(r3_en), .scan_enable(scan_enable)); +`else +wire mclk_r3 = mclk; +`endif + +always @(posedge mclk_r3 or posedge puc_rst) + if (puc_rst) r3 <= 16'h0000; +`ifdef CLOCK_GATING + else r3 <= reg_dest_val_in; +`else + else if (r3_wr) r3 <= reg_dest_val_in; +`endif + + +//============================================================================= +// 4) GENERAL PURPOSE REGISTERS (R4...R15) +//============================================================================= + +// R4 +//------------ +reg [15:0] r4; +wire r4_wr = inst_dest[4] & reg_dest_wr; +wire r4_inc = inst_src_in[4] & reg_incr; + +`ifdef CLOCK_GATING +wire r4_en = r4_wr | r4_inc; +wire mclk_r4; +omsp_clock_gate clock_gate_r4 (.gclk(mclk_r4), + .clk (mclk), .enable(r4_en), .scan_enable(scan_enable)); +`else +wire mclk_r4 = mclk; +`endif + +always @(posedge mclk_r4 or posedge puc_rst) + if (puc_rst) r4 <= 16'h0000; + else if (r4_wr) r4 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r4 <= reg_incr_val; +`else + else if (r4_inc) r4 <= reg_incr_val; +`endif + +// R5 +//------------ +reg [15:0] r5; +wire r5_wr = inst_dest[5] & reg_dest_wr; +wire r5_inc = inst_src_in[5] & reg_incr; + +`ifdef CLOCK_GATING +wire r5_en = r5_wr | r5_inc; +wire mclk_r5; +omsp_clock_gate clock_gate_r5 (.gclk(mclk_r5), + .clk (mclk), .enable(r5_en), .scan_enable(scan_enable)); +`else +wire mclk_r5 = mclk; +`endif + +always @(posedge mclk_r5 or posedge puc_rst) + if (puc_rst) r5 <= 16'h0000; + else if (r5_wr) r5 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r5 <= reg_incr_val; +`else + else if (r5_inc) r5 <= reg_incr_val; +`endif + +// R6 +//------------ +reg [15:0] r6; +wire r6_wr = inst_dest[6] & reg_dest_wr; +wire r6_inc = inst_src_in[6] & reg_incr; + +`ifdef CLOCK_GATING +wire r6_en = r6_wr | r6_inc; +wire mclk_r6; +omsp_clock_gate clock_gate_r6 (.gclk(mclk_r6), + .clk (mclk), .enable(r6_en), .scan_enable(scan_enable)); +`else +wire mclk_r6 = mclk; +`endif + +always @(posedge mclk_r6 or posedge puc_rst) + if (puc_rst) r6 <= 16'h0000; + else if (r6_wr) r6 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r6 <= reg_incr_val; +`else + else if (r6_inc) r6 <= reg_incr_val; +`endif + +// R7 +//------------ +reg [15:0] r7; +wire r7_wr = inst_dest[7] & reg_dest_wr; +wire r7_inc = inst_src_in[7] & reg_incr; + +`ifdef CLOCK_GATING +wire r7_en = r7_wr | r7_inc; +wire mclk_r7; +omsp_clock_gate clock_gate_r7 (.gclk(mclk_r7), + .clk (mclk), .enable(r7_en), .scan_enable(scan_enable)); +`else +wire mclk_r7 = mclk; +`endif + +always @(posedge mclk_r7 or posedge puc_rst) + if (puc_rst) r7 <= 16'h0000; + else if (r7_wr) r7 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r7 <= reg_incr_val; +`else + else if (r7_inc) r7 <= reg_incr_val; +`endif + +// R8 +//------------ +reg [15:0] r8; +wire r8_wr = inst_dest[8] & reg_dest_wr; +wire r8_inc = inst_src_in[8] & reg_incr; + +`ifdef CLOCK_GATING +wire r8_en = r8_wr | r8_inc; +wire mclk_r8; +omsp_clock_gate clock_gate_r8 (.gclk(mclk_r8), + .clk (mclk), .enable(r8_en), .scan_enable(scan_enable)); +`else +wire mclk_r8 = mclk; +`endif + +always @(posedge mclk_r8 or posedge puc_rst) + if (puc_rst) r8 <= 16'h0000; + else if (r8_wr) r8 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r8 <= reg_incr_val; +`else + else if (r8_inc) r8 <= reg_incr_val; +`endif + +// R9 +//------------ +reg [15:0] r9; +wire r9_wr = inst_dest[9] & reg_dest_wr; +wire r9_inc = inst_src_in[9] & reg_incr; + +`ifdef CLOCK_GATING +wire r9_en = r9_wr | r9_inc; +wire mclk_r9; +omsp_clock_gate clock_gate_r9 (.gclk(mclk_r9), + .clk (mclk), .enable(r9_en), .scan_enable(scan_enable)); +`else +wire mclk_r9 = mclk; +`endif + +always @(posedge mclk_r9 or posedge puc_rst) + if (puc_rst) r9 <= 16'h0000; + else if (r9_wr) r9 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r9 <= reg_incr_val; +`else + else if (r9_inc) r9 <= reg_incr_val; +`endif + +// R10 +//------------ +reg [15:0] r10; +wire r10_wr = inst_dest[10] & reg_dest_wr; +wire r10_inc = inst_src_in[10] & reg_incr; + +`ifdef CLOCK_GATING +wire r10_en = r10_wr | r10_inc; +wire mclk_r10; +omsp_clock_gate clock_gate_r10 (.gclk(mclk_r10), + .clk (mclk), .enable(r10_en), .scan_enable(scan_enable)); +`else +wire mclk_r10 = mclk; +`endif + +always @(posedge mclk_r10 or posedge puc_rst) + if (puc_rst) r10 <= 16'h0000; + else if (r10_wr) r10 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r10 <= reg_incr_val; +`else + else if (r10_inc) r10 <= reg_incr_val; +`endif + +// R11 +//------------ +reg [15:0] r11; +wire r11_wr = inst_dest[11] & reg_dest_wr; +wire r11_inc = inst_src_in[11] & reg_incr; + +`ifdef CLOCK_GATING +wire r11_en = r11_wr | r11_inc; +wire mclk_r11; +omsp_clock_gate clock_gate_r11 (.gclk(mclk_r11), + .clk (mclk), .enable(r11_en), .scan_enable(scan_enable)); +`else +wire mclk_r11 = mclk; +`endif + +always @(posedge mclk_r11 or posedge puc_rst) + if (puc_rst) r11 <= 16'h0000; + else if (r11_wr) r11 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r11 <= reg_incr_val; +`else + else if (r11_inc) r11 <= reg_incr_val; +`endif + +// R12 +//------------ +reg [15:0] r12; +wire r12_wr = inst_dest[12] & reg_dest_wr; +wire r12_inc = inst_src_in[12] & reg_incr; + +`ifdef CLOCK_GATING +wire r12_en = r12_wr | r12_inc; +wire mclk_r12; +omsp_clock_gate clock_gate_r12 (.gclk(mclk_r12), + .clk (mclk), .enable(r12_en), .scan_enable(scan_enable)); +`else +wire mclk_r12 = mclk; +`endif + +always @(posedge mclk_r12 or posedge puc_rst) + if (puc_rst) r12 <= 16'h0000; + else if (r12_wr) r12 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r12 <= reg_incr_val; +`else + else if (r12_inc) r12 <= reg_incr_val; +`endif + +// R13 +//------------ +reg [15:0] r13; +wire r13_wr = inst_dest[13] & reg_dest_wr; +wire r13_inc = inst_src_in[13] & reg_incr; + +`ifdef CLOCK_GATING +wire r13_en = r13_wr | r13_inc; +wire mclk_r13; +omsp_clock_gate clock_gate_r13 (.gclk(mclk_r13), + .clk (mclk), .enable(r13_en), .scan_enable(scan_enable)); +`else +wire mclk_r13 = mclk; +`endif + +always @(posedge mclk_r13 or posedge puc_rst) + if (puc_rst) r13 <= 16'h0000; + else if (r13_wr) r13 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r13 <= reg_incr_val; +`else + else if (r13_inc) r13 <= reg_incr_val; +`endif + +// R14 +//------------ +reg [15:0] r14; +wire r14_wr = inst_dest[14] & reg_dest_wr; +wire r14_inc = inst_src_in[14] & reg_incr; + +`ifdef CLOCK_GATING +wire r14_en = r14_wr | r14_inc; +wire mclk_r14; +omsp_clock_gate clock_gate_r14 (.gclk(mclk_r14), + .clk (mclk), .enable(r14_en), .scan_enable(scan_enable)); +`else +wire mclk_r14 = mclk; +`endif + +always @(posedge mclk_r14 or posedge puc_rst) + if (puc_rst) r14 <= 16'h0000; + else if (r14_wr) r14 <= reg_dest_val_in; +`ifdef CLOCK_GATING + else r14 <= reg_incr_val; +`else + else if (r14_inc) r14 <= reg_incr_val; +`endif + +// R15 +//------------ +reg [15:0] r15; +wire r15_wr = inst_dest[15] & reg_dest_wr; +wire r15_inc = inst_src_in[15] & reg_incr; + +`ifdef CLOCK_GATING +wire r15_en = r15_wr | r15_inc; +wire mclk_r15; +omsp_clock_gate clock_gate_r15 (.gclk(mclk_r15), + .clk (mclk), .enable(r15_en), .scan_enable(scan_enable)); +`else +wire mclk_r15 = mclk; +`endif + +always @(posedge mclk_r15 or posedge puc_rst) + if (puc_rst) r15 <= 16'h0000; + else if (r15_wr) r15 <= reg_dest_val_in; + `ifdef CLOCK_GATING + else r15 <= reg_incr_val; +`else + else if (r15_inc) r15 <= reg_incr_val; +`endif + + +//============================================================================= +// 5) READ MUX +//============================================================================= + +assign reg_src = (r0 & {16{inst_src_in[0]}}) | + (r1 & {16{inst_src_in[1]}}) | + (r2 & {16{inst_src_in[2]}}) | + (r3 & {16{inst_src_in[3]}}) | + (r4 & {16{inst_src_in[4]}}) | + (r5 & {16{inst_src_in[5]}}) | + (r6 & {16{inst_src_in[6]}}) | + (r7 & {16{inst_src_in[7]}}) | + (r8 & {16{inst_src_in[8]}}) | + (r9 & {16{inst_src_in[9]}}) | + (r10 & {16{inst_src_in[10]}}) | + (r11 & {16{inst_src_in[11]}}) | + (r12 & {16{inst_src_in[12]}}) | + (r13 & {16{inst_src_in[13]}}) | + (r14 & {16{inst_src_in[14]}}) | + (r15 & {16{inst_src_in[15]}}); + +assign reg_dest = (r0 & {16{inst_dest[0]}}) | + (r1 & {16{inst_dest[1]}}) | + (r2 & {16{inst_dest[2]}}) | + (r3 & {16{inst_dest[3]}}) | + (r4 & {16{inst_dest[4]}}) | + (r5 & {16{inst_dest[5]}}) | + (r6 & {16{inst_dest[6]}}) | + (r7 & {16{inst_dest[7]}}) | + (r8 & {16{inst_dest[8]}}) | + (r9 & {16{inst_dest[9]}}) | + (r10 & {16{inst_dest[10]}}) | + (r11 & {16{inst_dest[11]}}) | + (r12 & {16{inst_dest[12]}}) | + (r13 & {16{inst_dest[13]}}) | + (r14 & {16{inst_dest[14]}}) | + (r15 & {16{inst_dest[15]}}); + + +endmodule // omsp_register_file + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_scan_mux.v b/tests/openmsp430/rtl/omsp_scan_mux.v new file mode 100644 index 000000000..9a906474d --- /dev/null +++ b/tests/openmsp430/rtl/omsp_scan_mux.v @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_scan_mux.v +// +// *Module Description: +// Generic mux for scan mode +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_scan_mux ( + +// OUTPUTs + data_out, // Scan mux data output + +// INPUTs + data_in_scan, // Selected data input for scan mode + data_in_func, // Selected data input for functional mode + scan_mode // Scan mode +); + +// OUTPUTs +//========= +output data_out; // Scan mux data output + +// INPUTs +//========= +input data_in_scan; // Selected data input for scan mode +input data_in_func; // Selected data input for functional mode +input scan_mode; // Scan mode + + +//============================================================================= +// 1) SCAN MUX +//============================================================================= + +assign data_out = scan_mode ? data_in_scan : data_in_func; + + +endmodule // omsp_scan_mux + + diff --git a/tests/openmsp430/rtl/omsp_sfr.v b/tests/openmsp430/rtl/omsp_sfr.v new file mode 100644 index 000000000..bc8c11a5b --- /dev/null +++ b/tests/openmsp430/rtl/omsp_sfr.v @@ -0,0 +1,353 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_sfr.v +// +// *Module Description: +// Processor Special function register +// Non-Maskable Interrupt generation +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_sfr ( + +// OUTPUTs + cpu_id, // CPU ID + nmi_pnd, // NMI Pending + nmi_wkup, // NMI Wakeup + per_dout, // Peripheral data output + wdtie, // Watchdog-timer interrupt enable + wdtifg_sw_clr, // Watchdog-timer interrupt flag software clear + wdtifg_sw_set, // Watchdog-timer interrupt flag software set + +// INPUTs + mclk, // Main system clock + nmi, // Non-maskable interrupt (asynchronous) + nmi_acc, // Non-Maskable interrupt request accepted + per_addr, // Peripheral address + per_din, // Peripheral data input + per_en, // Peripheral enable (high active) + per_we, // Peripheral write enable (high active) + puc_rst, // Main system reset + scan_mode, // Scan mode + wdtifg, // Watchdog-timer interrupt flag + wdtnmies // Watchdog-timer NMI edge selection +); + +// OUTPUTs +//========= +output [31:0] cpu_id; // CPU ID +output nmi_pnd; // NMI Pending +output nmi_wkup; // NMI Wakeup +output [15:0] per_dout; // Peripheral data output +output wdtie; // Watchdog-timer interrupt enable +output wdtifg_sw_clr;// Watchdog-timer interrupt flag software clear +output wdtifg_sw_set;// Watchdog-timer interrupt flag software set + +// INPUTs +//========= +input mclk; // Main system clock +input nmi; // Non-maskable interrupt (asynchronous) +input nmi_acc; // Non-Maskable interrupt request accepted +input [13:0] per_addr; // Peripheral address +input [15:0] per_din; // Peripheral data input +input per_en; // Peripheral enable (high active) +input [1:0] per_we; // Peripheral write enable (high active) +input puc_rst; // Main system reset +input scan_mode; // Scan mode +input wdtifg; // Watchdog-timer interrupt flag +input wdtnmies; // Watchdog-timer NMI edge selection + + +//============================================================================= +// 1) PARAMETER DECLARATION +//============================================================================= + +// Register base address (must be aligned to decoder bit width) +parameter [14:0] BASE_ADDR = 15'h0000; + +// Decoder bit width (defines how many bits are considered for address decoding) +parameter DEC_WD = 3; + +// Register addresses offset +parameter [DEC_WD-1:0] IE1 = 'h0, + IFG1 = 'h2, + CPU_ID_LO = 'h4, + CPU_ID_HI = 'h6; + +// Register one-hot decoder utilities +parameter DEC_SZ = (1 << DEC_WD); +parameter [DEC_SZ-1:0] BASE_REG = {{DEC_SZ-1{1'b0}}, 1'b1}; + +// Register one-hot decoder +parameter [DEC_SZ-1:0] IE1_D = (BASE_REG << IE1), + IFG1_D = (BASE_REG << IFG1), + CPU_ID_LO_D = (BASE_REG << CPU_ID_LO), + CPU_ID_HI_D = (BASE_REG << CPU_ID_HI); + + +//============================================================================ +// 2) REGISTER DECODER +//============================================================================ + +// Local register selection +wire reg_sel = per_en & (per_addr[13:DEC_WD-1]==BASE_ADDR[14:DEC_WD]); + +// Register local address +wire [DEC_WD-1:0] reg_addr = {1'b0, per_addr[DEC_WD-2:0]}; + +// Register address decode +wire [DEC_SZ-1:0] reg_dec = (IE1_D & {DEC_SZ{(reg_addr==(IE1 >>1))}}) | + (IFG1_D & {DEC_SZ{(reg_addr==(IFG1 >>1))}}) | + (CPU_ID_LO_D & {DEC_SZ{(reg_addr==(CPU_ID_LO >>1))}}) | + (CPU_ID_HI_D & {DEC_SZ{(reg_addr==(CPU_ID_HI >>1))}}); + +// Read/Write probes +wire reg_lo_write = per_we[0] & reg_sel; +wire reg_hi_write = per_we[1] & reg_sel; +wire reg_read = ~|per_we & reg_sel; + +// Read/Write vectors +wire [DEC_SZ-1:0] reg_hi_wr = reg_dec & {DEC_SZ{reg_hi_write}}; +wire [DEC_SZ-1:0] reg_lo_wr = reg_dec & {DEC_SZ{reg_lo_write}}; +wire [DEC_SZ-1:0] reg_rd = reg_dec & {DEC_SZ{reg_read}}; + + +//============================================================================ +// 3) REGISTERS +//============================================================================ + +// IE1 Register +//-------------- +wire [7:0] ie1; +wire ie1_wr = IE1[0] ? reg_hi_wr[IE1] : reg_lo_wr[IE1]; +wire [7:0] ie1_nxt = IE1[0] ? per_din[15:8] : per_din[7:0]; + +`ifdef NMI +reg nmie; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) nmie <= 1'b0; + else if (nmi_acc) nmie <= 1'b0; + else if (ie1_wr) nmie <= ie1_nxt[4]; +`else +wire nmie = 1'b0; +`endif + +`ifdef WATCHDOG +reg wdtie; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) wdtie <= 1'b0; + else if (ie1_wr) wdtie <= ie1_nxt[0]; +`else +wire wdtie = 1'b0; +`endif + +assign ie1 = {3'b000, nmie, 3'b000, wdtie}; + + +// IFG1 Register +//--------------- +wire [7:0] ifg1; + +wire ifg1_wr = IFG1[0] ? reg_hi_wr[IFG1] : reg_lo_wr[IFG1]; +wire [7:0] ifg1_nxt = IFG1[0] ? per_din[15:8] : per_din[7:0]; + +`ifdef NMI +reg nmiifg; +wire nmi_edge; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) nmiifg <= 1'b0; + else if (nmi_edge) nmiifg <= 1'b1; + else if (ifg1_wr) nmiifg <= ifg1_nxt[4]; +`else +wire nmiifg = 1'b0; +`endif + +`ifdef WATCHDOG +assign wdtifg_sw_clr = ifg1_wr & ~ifg1_nxt[0]; +assign wdtifg_sw_set = ifg1_wr & ifg1_nxt[0]; +`else +assign wdtifg_sw_clr = 1'b0; +assign wdtifg_sw_set = 1'b0; +`endif + +assign ifg1 = {3'b000, nmiifg, 3'b000, wdtifg}; + + +// CPU_ID Register (READ ONLY) +//----------------------------- +// ------------------------------------------------------------------- +// CPU_ID_LO: | 15 14 13 12 11 10 9 | 8 7 6 5 4 | 3 | 2 1 0 | +// |----------------------------+-----------------+------+-------------| +// | PER_SPACE | USER_VERSION | ASIC | CPU_VERSION | +// -------------------------------------------------------------------- +// CPU_ID_HI: | 15 14 13 12 11 10 | 9 8 7 6 5 4 3 2 1 | 0 | +// |----------------------------+-------------------------------+------| +// | PMEM_SIZE | DMEM_SIZE | MPY | +// ------------------------------------------------------------------- + +wire [2:0] cpu_version = `CPU_VERSION; +`ifdef ASIC +wire cpu_asic = 1'b1; +`else +wire cpu_asic = 1'b0; +`endif +wire [4:0] user_version = `USER_VERSION; +wire [6:0] per_space = (`PER_SIZE >> 9); // cpu_id_per * 512 = peripheral space size +`ifdef MULTIPLIER +wire mpy_info = 1'b1; +`else +wire mpy_info = 1'b0; +`endif +wire [8:0] dmem_size = (`DMEM_SIZE >> 7); // cpu_id_dmem * 128 = data memory size +wire [5:0] pmem_size = (`PMEM_SIZE >> 10); // cpu_id_pmem * 1024 = program memory size + +assign cpu_id = {pmem_size, + dmem_size, + mpy_info, + per_space, + user_version, + cpu_asic, + cpu_version}; + + +//============================================================================ +// 4) DATA OUTPUT GENERATION +//============================================================================ + +// Data output mux +wire [15:0] ie1_rd = {8'h00, (ie1 & {8{reg_rd[IE1]}})} << (8 & {4{IE1[0]}}); +wire [15:0] ifg1_rd = {8'h00, (ifg1 & {8{reg_rd[IFG1]}})} << (8 & {4{IFG1[0]}}); +wire [15:0] cpu_id_lo_rd = cpu_id[15:0] & {16{reg_rd[CPU_ID_LO]}}; +wire [15:0] cpu_id_hi_rd = cpu_id[31:16] & {16{reg_rd[CPU_ID_HI]}}; + +wire [15:0] per_dout = ie1_rd | + ifg1_rd | + cpu_id_lo_rd | + cpu_id_hi_rd; + + +//============================================================================= +// 5) NMI GENERATION +//============================================================================= +// NOTE THAT THE NMI INPUT IS ASSUMED TO BE NON-GLITCHY +`ifdef NMI + +//----------------------------------- +// Edge selection +//----------------------------------- +wire nmi_pol = nmi ^ wdtnmies; + +//----------------------------------- +// Pulse capture and synchronization +//----------------------------------- +`ifdef SYNC_NMI + `ifdef ASIC + // Glitch free reset for the event capture + reg nmi_capture_rst; + always @(posedge mclk or posedge puc_rst) + if (puc_rst) nmi_capture_rst <= 1'b1; + else nmi_capture_rst <= ifg1_wr & ~ifg1_nxt[4]; + + // NMI event capture + wire nmi_capture; + omsp_wakeup_cell wakeup_cell_nmi ( + .wkup_out (nmi_capture), // Wakup signal (asynchronous) + .scan_clk (mclk), // Scan clock + .scan_mode (scan_mode), // Scan mode + .scan_rst (puc_rst), // Scan reset + .wkup_clear (nmi_capture_rst), // Glitch free wakeup event clear + .wkup_event (nmi_pol) // Glitch free asynchronous wakeup event + ); + `else + wire nmi_capture = nmi_pol; + `endif + + // Synchronization + wire nmi_s; + omsp_sync_cell sync_cell_nmi ( + .data_out (nmi_s), + .data_in (nmi_capture), + .clk (mclk), + .rst (puc_rst) + ); + +`else + wire nmi_capture = nmi_pol; + wire nmi_s = nmi_pol; +`endif + +//----------------------------------- +// NMI Pending flag +//----------------------------------- + +// Delay +reg nmi_dly; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) nmi_dly <= 1'b0; + else nmi_dly <= nmi_s; + +// Edge detection +assign nmi_edge = ~nmi_dly & nmi_s; + +// NMI pending +wire nmi_pnd = nmiifg & nmie; + +// NMI wakeup +`ifdef ASIC +wire nmi_wkup; +omsp_and_gate and_nmi_wkup (.y(nmi_wkup), .a(nmi_capture ^ nmi_dly), .b(nmie)); +`else +wire nmi_wkup = 1'b0; +`endif + +`else + +wire nmi_pnd = 1'b0; +wire nmi_wkup = 1'b0; + +`endif + +endmodule // omsp_sfr + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/omsp_sync_cell.v b/tests/openmsp430/rtl/omsp_sync_cell.v new file mode 100644 index 000000000..ece0682a9 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_sync_cell.v @@ -0,0 +1,80 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_sync_cell.v +// +// *Module Description: +// Generic synchronizer for the openMSP430 +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_sync_cell ( + +// OUTPUTs + data_out, // Synchronized data output + +// INPUTs + clk, // Receiving clock + data_in, // Asynchronous data input + rst // Receiving reset (active high) +); + +// OUTPUTs +//========= +output data_out; // Synchronized data output + +// INPUTs +//========= +input clk; // Receiving clock +input data_in; // Asynchronous data input +input rst; // Receiving reset (active high) + + +//============================================================================= +// 1) SYNCHRONIZER +//============================================================================= + +reg [1:0] data_sync; + +always @(posedge clk or posedge rst) + if (rst) data_sync <= 2'b00; + else data_sync <= {data_sync[0], data_in}; + +assign data_out = data_sync[1]; + + +endmodule // omsp_sync_cell + diff --git a/tests/openmsp430/rtl/omsp_sync_reset.v b/tests/openmsp430/rtl/omsp_sync_reset.v new file mode 100644 index 000000000..15a158b43 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_sync_reset.v @@ -0,0 +1,78 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_sync_reset.v +// +// *Module Description: +// Generic reset synchronizer for the openMSP430 +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_sync_reset ( + +// OUTPUTs + rst_s, // Synchronized reset + +// INPUTs + clk, // Receiving clock + rst_a // Asynchronous reset +); + +// OUTPUTs +//========= +output rst_s; // Synchronized reset + +// INPUTs +//========= +input clk; // Receiving clock +input rst_a; // Asynchronous reset + + +//============================================================================= +// 1) SYNCHRONIZER +//============================================================================= + +reg [1:0] data_sync; + +always @(posedge clk or posedge rst_a) + if (rst_a) data_sync <= 2'b11; + else data_sync <= {data_sync[0], 1'b0}; + +assign rst_s = data_sync[1]; + + +endmodule // omsp_sync_reset + diff --git a/tests/openmsp430/rtl/omsp_wakeup_cell.v b/tests/openmsp430/rtl/omsp_wakeup_cell.v new file mode 100644 index 000000000..6e5fcd880 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_wakeup_cell.v @@ -0,0 +1,108 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_wakeup_cell.v +// +// *Module Description: +// Generic Wakeup cell +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 103 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2011-03-05 15:44:48 +0100 (Sat, 05 Mar 2011) $ +//---------------------------------------------------------------------------- + +module omsp_wakeup_cell ( + +// OUTPUTs + wkup_out, // Wakup signal (asynchronous) + +// INPUTs + scan_clk, // Scan clock + scan_mode, // Scan mode + scan_rst, // Scan reset + wkup_clear, // Glitch free wakeup event clear + wkup_event // Glitch free asynchronous wakeup event +); + +// OUTPUTs +//========= +output wkup_out; // Wakup signal (asynchronous) + +// INPUTs +//========= +input scan_clk; // Scan clock +input scan_mode; // Scan mode +input scan_rst; // Scan reset +input wkup_clear; // Glitch free wakeup event clear +input wkup_event; // Glitch free asynchronous wakeup event + + +//============================================================================= +// 1) AND GATE +//============================================================================= + +// Scan stuff for the ASIC mode +`ifdef ASIC + wire wkup_rst; + omsp_scan_mux scan_mux_rst ( + .scan_mode (scan_mode), + .data_in_scan (scan_rst), + .data_in_func (wkup_clear), + .data_out (wkup_rst) + ); + + wire wkup_clk; + omsp_scan_mux scan_mux_clk ( + .scan_mode (scan_mode), + .data_in_scan (scan_clk), + .data_in_func (wkup_event), + .data_out (wkup_clk) + ); + +`else + wire wkup_rst = wkup_clear; + wire wkup_clk = wkup_event; +`endif + +// Wakeup capture +reg wkup_out; +always @(posedge wkup_clk or posedge wkup_rst) + if (wkup_rst) wkup_out <= 1'b0; + else wkup_out <= 1'b1; + + +endmodule // omsp_wakeup_cell + + + + diff --git a/tests/openmsp430/rtl/omsp_watchdog.v b/tests/openmsp430/rtl/omsp_watchdog.v new file mode 100644 index 000000000..f7cedda08 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_watchdog.v @@ -0,0 +1,556 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_watchdog.v +// +// *Module Description: +// Watchdog Timer +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_watchdog ( + +// OUTPUTs + per_dout, // Peripheral data output + wdt_irq, // Watchdog-timer interrupt + wdt_reset, // Watchdog-timer reset + wdt_wkup, // Watchdog Wakeup + wdtifg, // Watchdog-timer interrupt flag + wdtnmies, // Watchdog-timer NMI edge selection + +// INPUTs + aclk, // ACLK + aclk_en, // ACLK enable + dbg_freeze, // Freeze Watchdog counter + mclk, // Main system clock + per_addr, // Peripheral address + per_din, // Peripheral data input + per_en, // Peripheral enable (high active) + per_we, // Peripheral write enable (high active) + por, // Power-on reset + puc_rst, // Main system reset + scan_enable, // Scan enable (active during scan shifting) + scan_mode, // Scan mode + smclk, // SMCLK + smclk_en, // SMCLK enable + wdtie, // Watchdog timer interrupt enable + wdtifg_irq_clr, // Watchdog-timer interrupt flag irq accepted clear + wdtifg_sw_clr, // Watchdog-timer interrupt flag software clear + wdtifg_sw_set // Watchdog-timer interrupt flag software set +); + +// OUTPUTs +//========= +output [15:0] per_dout; // Peripheral data output +output wdt_irq; // Watchdog-timer interrupt +output wdt_reset; // Watchdog-timer reset +output wdt_wkup; // Watchdog Wakeup +output wdtifg; // Watchdog-timer interrupt flag +output wdtnmies; // Watchdog-timer NMI edge selection + +// INPUTs +//========= +input aclk; // ACLK +input aclk_en; // ACLK enable +input dbg_freeze; // Freeze Watchdog counter +input mclk; // Main system clock +input [13:0] per_addr; // Peripheral address +input [15:0] per_din; // Peripheral data input +input per_en; // Peripheral enable (high active) +input [1:0] per_we; // Peripheral write enable (high active) +input por; // Power-on reset +input puc_rst; // Main system reset +input scan_enable; // Scan enable (active during scan shifting) +input scan_mode; // Scan mode +input smclk; // SMCLK +input smclk_en; // SMCLK enable +input wdtie; // Watchdog timer interrupt enable +input wdtifg_irq_clr; // Clear Watchdog-timer interrupt flag +input wdtifg_sw_clr; // Watchdog-timer interrupt flag software clear +input wdtifg_sw_set; // Watchdog-timer interrupt flag software set + + +//============================================================================= +// 1) PARAMETER DECLARATION +//============================================================================= + +// Register base address (must be aligned to decoder bit width) +parameter [14:0] BASE_ADDR = 15'h0120; + +// Decoder bit width (defines how many bits are considered for address decoding) +parameter DEC_WD = 2; + +// Register addresses offset +parameter [DEC_WD-1:0] WDTCTL = 'h0; + +// Register one-hot decoder utilities +parameter DEC_SZ = (1 << DEC_WD); +parameter [DEC_SZ-1:0] BASE_REG = {{DEC_SZ-1{1'b0}}, 1'b1}; + +// Register one-hot decoder +parameter [DEC_SZ-1:0] WDTCTL_D = (BASE_REG << WDTCTL); + + +//============================================================================ +// 2) REGISTER DECODER +//============================================================================ + +// Local register selection +wire reg_sel = per_en & (per_addr[13:DEC_WD-1]==BASE_ADDR[14:DEC_WD]); + +// Register local address +wire [DEC_WD-1:0] reg_addr = {per_addr[DEC_WD-2:0], 1'b0}; + +// Register address decode +wire [DEC_SZ-1:0] reg_dec = (WDTCTL_D & {DEC_SZ{(reg_addr==WDTCTL)}}); + +// Read/Write probes +wire reg_write = |per_we & reg_sel; +wire reg_read = ~|per_we & reg_sel; + +// Read/Write vectors +wire [DEC_SZ-1:0] reg_wr = reg_dec & {DEC_SZ{reg_write}}; +wire [DEC_SZ-1:0] reg_rd = reg_dec & {DEC_SZ{reg_read}}; + + +//============================================================================ +// 3) REGISTERS +//============================================================================ + +// WDTCTL Register +//----------------- +// WDTNMI is not implemented and therefore masked + +reg [7:0] wdtctl; + +wire wdtctl_wr = reg_wr[WDTCTL]; + +`ifdef CLOCK_GATING +wire mclk_wdtctl; +omsp_clock_gate clock_gate_wdtctl (.gclk(mclk_wdtctl), + .clk (mclk), .enable(wdtctl_wr), .scan_enable(scan_enable)); +`else +wire mclk_wdtctl = mclk; +`endif + +`ifdef NMI +parameter [7:0] WDTNMIES_MASK = 8'h40; +`else +parameter [7:0] WDTNMIES_MASK = 8'h00; +`endif + +`ifdef ASIC + `ifdef WATCHDOG_MUX +parameter [7:0] WDTSSEL_MASK = 8'h04; + `else +parameter [7:0] WDTSSEL_MASK = 8'h00; + `endif +`else +parameter [7:0] WDTSSEL_MASK = 8'h04; +`endif + +parameter [7:0] WDTCTL_MASK = (8'b1001_0011 | WDTSSEL_MASK | WDTNMIES_MASK); + +always @ (posedge mclk_wdtctl or posedge puc_rst) + if (puc_rst) wdtctl <= 8'h00; +`ifdef CLOCK_GATING + else wdtctl <= per_din[7:0] & WDTCTL_MASK; +`else + else if (wdtctl_wr) wdtctl <= per_din[7:0] & WDTCTL_MASK; +`endif + +wire wdtpw_error = wdtctl_wr & (per_din[15:8]!=8'h5a); +wire wdttmsel = wdtctl[4]; +wire wdtnmies = wdtctl[6]; + + +//============================================================================ +// 4) DATA OUTPUT GENERATION +//============================================================================ + +`ifdef NMI +parameter [7:0] WDTNMI_RD_MASK = 8'h20; +`else +parameter [7:0] WDTNMI_RD_MASK = 8'h00; +`endif +`ifdef WATCHDOG_MUX +parameter [7:0] WDTSSEL_RD_MASK = 8'h00; +`else + `ifdef WATCHDOG_NOMUX_ACLK +parameter [7:0] WDTSSEL_RD_MASK = 8'h04; + `else +parameter [7:0] WDTSSEL_RD_MASK = 8'h00; + `endif +`endif +parameter [7:0] WDTCTL_RD_MASK = WDTNMI_RD_MASK | WDTSSEL_RD_MASK; + +// Data output mux +wire [15:0] wdtctl_rd = {8'h69, wdtctl | WDTCTL_RD_MASK} & {16{reg_rd[WDTCTL]}}; +wire [15:0] per_dout = wdtctl_rd; + + +//============================================================================= +// 5) WATCHDOG TIMER (ASIC IMPLEMENTATION) +//============================================================================= +`ifdef ASIC + +// Watchdog clock source selection +//--------------------------------- +wire wdt_clk; + +`ifdef WATCHDOG_MUX +omsp_clock_mux clock_mux_watchdog ( + .clk_out (wdt_clk), + .clk_in0 (smclk), + .clk_in1 (aclk), + .reset (puc_rst), + .scan_mode (scan_mode), + .select (wdtctl[2]) +); +`else + `ifdef WATCHDOG_NOMUX_ACLK + assign wdt_clk = aclk; + `else + assign wdt_clk = smclk; + `endif +`endif + +// Reset synchronizer for the watchdog local clock domain +//-------------------------------------------------------- + +wire wdt_rst_noscan; +wire wdt_rst; + +// Reset Synchronizer +omsp_sync_reset sync_reset_por ( + .rst_s (wdt_rst_noscan), + .clk (wdt_clk), + .rst_a (puc_rst) +); + +// Scan Reset Mux +omsp_scan_mux scan_mux_wdt_rst ( + .scan_mode (scan_mode), + .data_in_scan (puc_rst), + .data_in_func (wdt_rst_noscan), + .data_out (wdt_rst) +); + + +// Watchog counter clear (synchronization) +//----------------------------------------- + +// Toggle bit whenever the watchog needs to be cleared +reg wdtcnt_clr_toggle; +wire wdtcnt_clr_detect = (wdtctl_wr & per_din[3]); +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) wdtcnt_clr_toggle <= 1'b0; + else if (wdtcnt_clr_detect) wdtcnt_clr_toggle <= ~wdtcnt_clr_toggle; + +// Synchronization +wire wdtcnt_clr_sync; +omsp_sync_cell sync_cell_wdtcnt_clr ( + .data_out (wdtcnt_clr_sync), + .data_in (wdtcnt_clr_toggle), + .clk (wdt_clk), + .rst (wdt_rst) +); + +// Edge detection +reg wdtcnt_clr_sync_dly; +always @ (posedge wdt_clk or posedge wdt_rst) + if (wdt_rst) wdtcnt_clr_sync_dly <= 1'b0; + else wdtcnt_clr_sync_dly <= wdtcnt_clr_sync; + +wire wdtqn_edge; +wire wdtcnt_clr = (wdtcnt_clr_sync ^ wdtcnt_clr_sync_dly) | wdtqn_edge; + + +// Watchog counter increment (synchronization) +//---------------------------------------------- +wire wdtcnt_incr; + +omsp_sync_cell sync_cell_wdtcnt_incr ( + .data_out (wdtcnt_incr), + .data_in (~wdtctl[7] & ~dbg_freeze), + .clk (wdt_clk), + .rst (wdt_rst) +); + + +// Watchdog 16 bit counter +//-------------------------- +reg [15:0] wdtcnt; + +wire [15:0] wdtcnt_nxt = wdtcnt+16'h0001; + +`ifdef CLOCK_GATING +wire wdtcnt_en = wdtcnt_clr | wdtcnt_incr; +wire wdt_clk_cnt; +omsp_clock_gate clock_gate_wdtcnt (.gclk(wdt_clk_cnt), + .clk (wdt_clk), .enable(wdtcnt_en), .scan_enable(scan_enable)); +`else +wire wdt_clk_cnt = wdt_clk; +`endif + +always @ (posedge wdt_clk_cnt or posedge wdt_rst) + if (wdt_rst) wdtcnt <= 16'h0000; + else if (wdtcnt_clr) wdtcnt <= 16'h0000; +`ifdef CLOCK_GATING + else wdtcnt <= wdtcnt_nxt; +`else + else if (wdtcnt_incr) wdtcnt <= wdtcnt_nxt; +`endif + + +// Local synchronizer for the wdtctl.WDTISx +// configuration (note that we can live with +// a full bus synchronizer as it won't hurt +// if we get a wrong WDTISx value for a +// single clock cycle) +//-------------------------------------------- +reg [1:0] wdtisx_s; +reg [1:0] wdtisx_ss; +always @ (posedge wdt_clk_cnt or posedge wdt_rst) + if (wdt_rst) + begin + wdtisx_s <= 2'h0; + wdtisx_ss <= 2'h0; + end + else + begin + wdtisx_s <= wdtctl[1:0]; + wdtisx_ss <= wdtisx_s; + end + + +// Interval selection mux +//-------------------------- +reg wdtqn; + +always @(wdtisx_ss or wdtcnt_nxt) + case(wdtisx_ss) + 2'b00 : wdtqn = wdtcnt_nxt[15]; + 2'b01 : wdtqn = wdtcnt_nxt[13]; + 2'b10 : wdtqn = wdtcnt_nxt[9]; + default: wdtqn = wdtcnt_nxt[6]; + endcase + + +// Watchdog event detection +//----------------------------- + +// Interval end detection +assign wdtqn_edge = (wdtqn & wdtcnt_incr); + +// Toggle bit for the transmition to the MCLK domain +reg wdt_evt_toggle; +always @ (posedge wdt_clk_cnt or posedge wdt_rst) + if (wdt_rst) wdt_evt_toggle <= 1'b0; + else if (wdtqn_edge) wdt_evt_toggle <= ~wdt_evt_toggle; + +// Synchronize in the MCLK domain +wire wdt_evt_toggle_sync; +omsp_sync_cell sync_cell_wdt_evt ( + .data_out (wdt_evt_toggle_sync), + .data_in (wdt_evt_toggle), + .clk (mclk), + .rst (puc_rst) +); + +// Delay for edge detection of the toggle bit +reg wdt_evt_toggle_sync_dly; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) wdt_evt_toggle_sync_dly <= 1'b0; + else wdt_evt_toggle_sync_dly <= wdt_evt_toggle_sync; + +wire wdtifg_evt = (wdt_evt_toggle_sync_dly ^ wdt_evt_toggle_sync) | wdtpw_error; + + +// Watchdog wakeup generation +//------------------------------------------------------------- + +// Clear wakeup when the watchdog flag is cleared (glitch free) +reg wdtifg_clr_reg; +wire wdtifg_clr; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) wdtifg_clr_reg <= 1'b1; + else wdtifg_clr_reg <= wdtifg_clr; + +// Set wakeup when the watchdog event is detected (glitch free) +reg wdtqn_edge_reg; +always @ (posedge wdt_clk_cnt or posedge wdt_rst) + if (wdt_rst) wdtqn_edge_reg <= 1'b0; + else wdtqn_edge_reg <= wdtqn_edge; + +// Watchdog wakeup cell +wire wdt_wkup_pre; +omsp_wakeup_cell wakeup_cell_wdog ( + .wkup_out (wdt_wkup_pre), // Wakup signal (asynchronous) + .scan_clk (mclk), // Scan clock + .scan_mode (scan_mode), // Scan mode + .scan_rst (puc_rst), // Scan reset + .wkup_clear (wdtifg_clr_reg), // Glitch free wakeup event clear + .wkup_event (wdtqn_edge_reg) // Glitch free asynchronous wakeup event +); + +// When not in HOLD, the watchdog can generate a wakeup when: +// - in interval mode (if interrupts are enabled) +// - in reset mode (always) +reg wdt_wkup_en; +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) wdt_wkup_en <= 1'b0; + else wdt_wkup_en <= ~wdtctl[7] & (~wdttmsel | (wdttmsel & wdtie)); + +// Make wakeup when not enabled +wire wdt_wkup; +omsp_and_gate and_wdt_wkup (.y(wdt_wkup), .a(wdt_wkup_pre), .b(wdt_wkup_en)); + + +// Watchdog interrupt flag +//------------------------------ +reg wdtifg; + +wire wdtifg_set = wdtifg_evt | wdtifg_sw_set; +assign wdtifg_clr = (wdtifg_irq_clr & wdttmsel) | wdtifg_sw_clr; + +always @ (posedge mclk or posedge por) + if (por) wdtifg <= 1'b0; + else if (wdtifg_set) wdtifg <= 1'b1; + else if (wdtifg_clr) wdtifg <= 1'b0; + + +// Watchdog interrupt generation +//--------------------------------- +wire wdt_irq = wdttmsel & wdtifg & wdtie; + + +// Watchdog reset generation +//----------------------------- +reg wdt_reset; + +always @ (posedge mclk or posedge por) + if (por) wdt_reset <= 1'b0; + else wdt_reset <= wdtpw_error | (wdtifg_set & ~wdttmsel); + + + +//============================================================================= +// 6) WATCHDOG TIMER (FPGA IMPLEMENTATION) +//============================================================================= +`else + +// Watchdog clock source selection +//--------------------------------- +wire clk_src_en = wdtctl[2] ? aclk_en : smclk_en; + + +// Watchdog 16 bit counter +//-------------------------- +reg [15:0] wdtcnt; + +wire wdtifg_evt; +wire wdtcnt_clr = (wdtctl_wr & per_din[3]) | wdtifg_evt; +wire wdtcnt_incr = ~wdtctl[7] & clk_src_en & ~dbg_freeze; + +wire [15:0] wdtcnt_nxt = wdtcnt+16'h0001; + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) wdtcnt <= 16'h0000; + else if (wdtcnt_clr) wdtcnt <= 16'h0000; + else if (wdtcnt_incr) wdtcnt <= wdtcnt_nxt; + + +// Interval selection mux +//-------------------------- +reg wdtqn; + +always @(wdtctl or wdtcnt_nxt) + case(wdtctl[1:0]) + 2'b00 : wdtqn = wdtcnt_nxt[15]; + 2'b01 : wdtqn = wdtcnt_nxt[13]; + 2'b10 : wdtqn = wdtcnt_nxt[9]; + default: wdtqn = wdtcnt_nxt[6]; + endcase + + +// Watchdog event detection +//----------------------------- + +assign wdtifg_evt = (wdtqn & wdtcnt_incr) | wdtpw_error; + + +// Watchdog interrupt flag +//------------------------------ +reg wdtifg; + +wire wdtifg_set = wdtifg_evt | wdtifg_sw_set; +wire wdtifg_clr = (wdtifg_irq_clr & wdttmsel) | wdtifg_sw_clr; + +always @ (posedge mclk or posedge por) + if (por) wdtifg <= 1'b0; + else if (wdtifg_set) wdtifg <= 1'b1; + else if (wdtifg_clr) wdtifg <= 1'b0; + + +// Watchdog interrupt generation +//--------------------------------- +wire wdt_irq = wdttmsel & wdtifg & wdtie; +wire wdt_wkup = 1'b0; + + +// Watchdog reset generation +//----------------------------- +reg wdt_reset; + +always @ (posedge mclk or posedge por) + if (por) wdt_reset <= 1'b0; + else wdt_reset <= wdtpw_error | (wdtifg_set & ~wdttmsel); + + +`endif + + +endmodule // omsp_watchdog + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/openMSP430.v b/tests/openmsp430/rtl/openMSP430.v new file mode 100644 index 000000000..938548aaf --- /dev/null +++ b/tests/openmsp430/rtl/openMSP430.v @@ -0,0 +1,584 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: openMSP430.v +// +// *Module Description: +// openMSP430 Top level file +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module openMSP430 ( + +// OUTPUTs + aclk, // ASIC ONLY: ACLK + aclk_en, // FPGA ONLY: ACLK enable + dbg_freeze, // Freeze peripherals + dbg_uart_txd, // Debug interface: UART TXD + dco_enable, // ASIC ONLY: Fast oscillator enable + dco_wkup, // ASIC ONLY: Fast oscillator wake-up (asynchronous) + dmem_addr, // Data Memory address + dmem_cen, // Data Memory chip enable (low active) + dmem_din, // Data Memory data input + dmem_wen, // Data Memory write enable (low active) + irq_acc, // Interrupt request accepted (one-hot signal) + lfxt_enable, // ASIC ONLY: Low frequency oscillator enable + lfxt_wkup, // ASIC ONLY: Low frequency oscillator wake-up (asynchronous) + mclk, // Main system clock + per_addr, // Peripheral address + per_din, // Peripheral data input + per_we, // Peripheral write enable (high active) + per_en, // Peripheral enable (high active) + pmem_addr, // Program Memory address + pmem_cen, // Program Memory chip enable (low active) + pmem_din, // Program Memory data input (optional) + pmem_wen, // Program Memory write enable (low active) (optional) + puc_rst, // Main system reset + smclk, // ASIC ONLY: SMCLK + smclk_en, // FPGA ONLY: SMCLK enable + +// INPUTs + cpu_en, // Enable CPU code execution (asynchronous and non-glitchy) + dbg_en, // Debug interface enable (asynchronous and non-glitchy) + dbg_uart_rxd, // Debug interface: UART RXD (asynchronous) + dco_clk, // Fast oscillator (fast clock) + dmem_dout, // Data Memory data output + irq, // Maskable interrupts + lfxt_clk, // Low frequency oscillator (typ 32kHz) + nmi, // Non-maskable interrupt (asynchronous) + per_dout, // Peripheral data output + pmem_dout, // Program Memory data output + reset_n, // Reset Pin (low active, asynchronous and non-glitchy) + scan_enable, // ASIC ONLY: Scan enable (active during scan shifting) + scan_mode, // ASIC ONLY: Scan mode + wkup // ASIC ONLY: System Wake-up (asynchronous and non-glitchy) +); + +// OUTPUTs +//========= +output aclk; // ASIC ONLY: ACLK +output aclk_en; // FPGA ONLY: ACLK enable +output dbg_freeze; // Freeze peripherals +output dbg_uart_txd; // Debug interface: UART TXD +output dco_enable; // ASIC ONLY: Fast oscillator enable +output dco_wkup; // ASIC ONLY: Fast oscillator wake-up (asynchronous) +output [`DMEM_MSB:0] dmem_addr; // Data Memory address +output dmem_cen; // Data Memory chip enable (low active) +output [15:0] dmem_din; // Data Memory data input +output [1:0] dmem_wen; // Data Memory write enable (low active) +output [13:0] irq_acc; // Interrupt request accepted (one-hot signal) +output lfxt_enable; // ASIC ONLY: Low frequency oscillator enable +output lfxt_wkup; // ASIC ONLY: Low frequency oscillator wake-up (asynchronous) +output mclk; // Main system clock +output [13:0] per_addr; // Peripheral address +output [15:0] per_din; // Peripheral data input +output [1:0] per_we; // Peripheral write enable (high active) +output per_en; // Peripheral enable (high active) +output [`PMEM_MSB:0] pmem_addr; // Program Memory address +output pmem_cen; // Program Memory chip enable (low active) +output [15:0] pmem_din; // Program Memory data input (optional) +output [1:0] pmem_wen; // Program Memory write enable (low active) (optional) +output puc_rst; // Main system reset +output smclk; // ASIC ONLY: SMCLK +output smclk_en; // FPGA ONLY: SMCLK enable + + +// INPUTs +//========= +input cpu_en; // Enable CPU code execution (asynchronous and non-glitchy) +input dbg_en; // Debug interface enable (asynchronous and non-glitchy) +input dbg_uart_rxd; // Debug interface: UART RXD (asynchronous) +input dco_clk; // Fast oscillator (fast clock) +input [15:0] dmem_dout; // Data Memory data output +input [13:0] irq; // Maskable interrupts +input lfxt_clk; // Low frequency oscillator (typ 32kHz) +input nmi; // Non-maskable interrupt (asynchronous and non-glitchy) +input [15:0] per_dout; // Peripheral data output +input [15:0] pmem_dout; // Program Memory data output +input reset_n; // Reset Pin (active low, asynchronous and non-glitchy) +input scan_enable; // ASIC ONLY: Scan enable (active during scan shifting) +input scan_mode; // ASIC ONLY: Scan mode +input wkup; // ASIC ONLY: System Wake-up (asynchronous and non-glitchy) + + + +//============================================================================= +// 1) INTERNAL WIRES/REGISTERS/PARAMETERS DECLARATION +//============================================================================= + +wire [7:0] inst_ad; +wire [7:0] inst_as; +wire [11:0] inst_alu; +wire inst_bw; +wire inst_irq_rst; +wire inst_mov; +wire [15:0] inst_dest; +wire [15:0] inst_dext; +wire [15:0] inst_sext; +wire [7:0] inst_so; +wire [15:0] inst_src; +wire [2:0] inst_type; +wire [7:0] inst_jmp; +wire [3:0] e_state; +wire exec_done; +wire decode_noirq; +wire cpu_en_s; +wire cpuoff; +wire oscoff; +wire scg0; +wire scg1; +wire por; +wire gie; +wire mclk_enable; +wire mclk_wkup; +wire [31:0] cpu_id; + +wire [15:0] eu_mab; +wire [15:0] eu_mdb_in; +wire [15:0] eu_mdb_out; +wire [1:0] eu_mb_wr; +wire eu_mb_en; +wire [15:0] fe_mab; +wire [15:0] fe_mdb_in; +wire fe_mb_en; +wire fe_pmem_wait; + +wire pc_sw_wr; +wire [15:0] pc_sw; +wire [15:0] pc; +wire [15:0] pc_nxt; + +wire nmi_acc; +wire nmi_pnd; +wire nmi_wkup; + +wire wdtie; +wire wdtnmies; +wire wdtifg; +wire wdt_irq; +wire wdt_wkup; +wire wdt_reset; +wire wdtifg_sw_clr; +wire wdtifg_sw_set; + +wire dbg_clk; +wire dbg_rst; +wire dbg_en_s; +wire dbg_halt_st; +wire dbg_halt_cmd; +wire dbg_mem_en; +wire dbg_reg_wr; +wire dbg_cpu_reset; +wire [15:0] dbg_mem_addr; +wire [15:0] dbg_mem_dout; +wire [15:0] dbg_mem_din; +wire [15:0] dbg_reg_din; +wire [1:0] dbg_mem_wr; +wire puc_pnd_set; + +wire [15:0] per_dout_or; +wire [15:0] per_dout_sfr; +wire [15:0] per_dout_wdog; +wire [15:0] per_dout_mpy; +wire [15:0] per_dout_clk; + + +//============================================================================= +// 2) GLOBAL CLOCK & RESET MANAGEMENT +//============================================================================= + +omsp_clock_module clock_module_0 ( + +// OUTPUTs + .aclk (aclk), // ACLK + .aclk_en (aclk_en), // ACLK enablex + .cpu_en_s (cpu_en_s), // Enable CPU code execution (synchronous) + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_en_s (dbg_en_s), // Debug interface enable (synchronous) + .dbg_rst (dbg_rst), // Debug unit reset + .dco_enable (dco_enable), // Fast oscillator enable + .dco_wkup (dco_wkup), // Fast oscillator wake-up (asynchronous) + .lfxt_enable (lfxt_enable), // Low frequency oscillator enable + .lfxt_wkup (lfxt_wkup), // Low frequency oscillator wake-up (asynchronous) + .mclk (mclk), // Main system clock + .per_dout (per_dout_clk), // Peripheral data output + .por (por), // Power-on reset + .puc_pnd_set (puc_pnd_set), // PUC pending set for the serial debug interface + .puc_rst (puc_rst), // Main system reset + .smclk (smclk), // SMCLK + .smclk_en (smclk_en), // SMCLK enable + +// INPUTs + .cpu_en (cpu_en), // Enable CPU code execution (asynchronous) + .cpuoff (cpuoff), // Turns off the CPU + .dbg_cpu_reset(dbg_cpu_reset), // Reset CPU from debug interface + .dbg_en (dbg_en), // Debug interface enable (asynchronous) + .dco_clk (dco_clk), // Fast oscillator (fast clock) + .lfxt_clk (lfxt_clk), // Low frequency oscillator (typ 32kHz) + .mclk_enable (mclk_enable), // Main System Clock enable + .mclk_wkup (mclk_wkup), // Main System Clock wake-up (asynchronous) + .oscoff (oscoff), // Turns off LFXT1 clock input + .per_addr (per_addr), // Peripheral address + .per_din (per_din), // Peripheral data input + .per_en (per_en), // Peripheral enable (high active) + .per_we (per_we), // Peripheral write enable (high active) + .reset_n (reset_n), // Reset Pin (low active, asynchronous) + .scan_enable (scan_enable), // Scan enable (active during scan shifting) + .scan_mode (scan_mode), // Scan mode + .scg0 (scg0), // System clock generator 1. Turns off the DCO + .scg1 (scg1), // System clock generator 1. Turns off the SMCLK + .wdt_reset (wdt_reset) // Watchdog-timer reset +); + + +//============================================================================= +// 3) FRONTEND (<=> FETCH & DECODE) +//============================================================================= + +omsp_frontend frontend_0 ( + +// OUTPUTs + .dbg_halt_st (dbg_halt_st), // Halt/Run status from CPU + .decode_noirq (decode_noirq), // Frontend decode instruction + .e_state (e_state), // Execution state + .exec_done (exec_done), // Execution completed + .inst_ad (inst_ad), // Decoded Inst: destination addressing mode + .inst_as (inst_as), // Decoded Inst: source addressing mode + .inst_alu (inst_alu), // ALU control signals + .inst_bw (inst_bw), // Decoded Inst: byte width + .inst_dest (inst_dest), // Decoded Inst: destination (one hot) + .inst_dext (inst_dext), // Decoded Inst: destination extended instruction word + .inst_irq_rst (inst_irq_rst), // Decoded Inst: Reset interrupt + .inst_jmp (inst_jmp), // Decoded Inst: Conditional jump + .inst_mov (inst_mov), // Decoded Inst: mov instruction + .inst_sext (inst_sext), // Decoded Inst: source extended instruction word + .inst_so (inst_so), // Decoded Inst: Single-operand arithmetic + .inst_src (inst_src), // Decoded Inst: source (one hot) + .inst_type (inst_type), // Decoded Instruction type + .irq_acc (irq_acc), // Interrupt request accepted + .mab (fe_mab), // Frontend Memory address bus + .mb_en (fe_mb_en), // Frontend Memory bus enable + .mclk_enable (mclk_enable), // Main System Clock enable + .mclk_wkup (mclk_wkup), // Main System Clock wake-up (asynchronous) + .nmi_acc (nmi_acc), // Non-Maskable interrupt request accepted + .pc (pc), // Program counter + .pc_nxt (pc_nxt), // Next PC value (for CALL & IRQ) + +// INPUTs + .cpu_en_s (cpu_en_s), // Enable CPU code execution (synchronous) + .cpuoff (cpuoff), // Turns off the CPU + .dbg_halt_cmd (dbg_halt_cmd), // Halt CPU command + .dbg_reg_sel (dbg_mem_addr[3:0]), // Debug selected register for rd/wr access + .fe_pmem_wait (fe_pmem_wait), // Frontend wait for Instruction fetch + .gie (gie), // General interrupt enable + .irq (irq), // Maskable interrupts + .mclk (mclk), // Main system clock + .mdb_in (fe_mdb_in), // Frontend Memory data bus input + .nmi_pnd (nmi_pnd), // Non-maskable interrupt pending + .nmi_wkup (nmi_wkup), // NMI Wakeup + .pc_sw (pc_sw), // Program counter software value + .pc_sw_wr (pc_sw_wr), // Program counter software write + .puc_rst (puc_rst), // Main system reset + .scan_enable (scan_enable), // Scan enable (active during scan shifting) + .wdt_irq (wdt_irq), // Watchdog-timer interrupt + .wdt_wkup (wdt_wkup), // Watchdog Wakeup + .wkup (wkup) // System Wake-up (asynchronous) +); + + +//============================================================================= +// 4) EXECUTION UNIT +//============================================================================= + +omsp_execution_unit execution_unit_0 ( + +// OUTPUTs + .cpuoff (cpuoff), // Turns off the CPU + .dbg_reg_din (dbg_reg_din), // Debug unit CPU register data input + .mab (eu_mab), // Memory address bus + .mb_en (eu_mb_en), // Memory bus enable + .mb_wr (eu_mb_wr), // Memory bus write transfer + .mdb_out (eu_mdb_out), // Memory data bus output + .oscoff (oscoff), // Turns off LFXT1 clock input + .pc_sw (pc_sw), // Program counter software value + .pc_sw_wr (pc_sw_wr), // Program counter software write + .scg0 (scg0), // System clock generator 1. Turns off the DCO + .scg1 (scg1), // System clock generator 1. Turns off the SMCLK + +// INPUTs + .dbg_halt_st (dbg_halt_st), // Halt/Run status from CPU + .dbg_mem_dout (dbg_mem_dout), // Debug unit data output + .dbg_reg_wr (dbg_reg_wr), // Debug unit CPU register write + .e_state (e_state), // Execution state + .exec_done (exec_done), // Execution completed + .gie (gie), // General interrupt enable + .inst_ad (inst_ad), // Decoded Inst: destination addressing mode + .inst_as (inst_as), // Decoded Inst: source addressing mode + .inst_alu (inst_alu), // ALU control signals + .inst_bw (inst_bw), // Decoded Inst: byte width + .inst_dest (inst_dest), // Decoded Inst: destination (one hot) + .inst_dext (inst_dext), // Decoded Inst: destination extended instruction word + .inst_irq_rst (inst_irq_rst), // Decoded Inst: reset interrupt + .inst_jmp (inst_jmp), // Decoded Inst: Conditional jump + .inst_mov (inst_mov), // Decoded Inst: mov instruction + .inst_sext (inst_sext), // Decoded Inst: source extended instruction word + .inst_so (inst_so), // Decoded Inst: Single-operand arithmetic + .inst_src (inst_src), // Decoded Inst: source (one hot) + .inst_type (inst_type), // Decoded Instruction type + .mclk (mclk), // Main system clock + .mdb_in (eu_mdb_in), // Memory data bus input + .pc (pc), // Program counter + .pc_nxt (pc_nxt), // Next PC value (for CALL & IRQ) + .puc_rst (puc_rst), // Main system reset + .scan_enable (scan_enable) // Scan enable (active during scan shifting) +); + + +//============================================================================= +// 5) MEMORY BACKBONE +//============================================================================= + +omsp_mem_backbone mem_backbone_0 ( + +// OUTPUTs + .dbg_mem_din (dbg_mem_din), // Debug unit Memory data input + .dmem_addr (dmem_addr), // Data Memory address + .dmem_cen (dmem_cen), // Data Memory chip enable (low active) + .dmem_din (dmem_din), // Data Memory data input + .dmem_wen (dmem_wen), // Data Memory write enable (low active) + .eu_mdb_in (eu_mdb_in), // Execution Unit Memory data bus input + .fe_mdb_in (fe_mdb_in), // Frontend Memory data bus input + .fe_pmem_wait (fe_pmem_wait), // Frontend wait for Instruction fetch + .per_addr (per_addr), // Peripheral address + .per_din (per_din), // Peripheral data input + .per_we (per_we), // Peripheral write enable (high active) + .per_en (per_en), // Peripheral enable (high active) + .pmem_addr (pmem_addr), // Program Memory address + .pmem_cen (pmem_cen), // Program Memory chip enable (low active) + .pmem_din (pmem_din), // Program Memory data input (optional) + .pmem_wen (pmem_wen), // Program Memory write enable (low active) (optional) + +// INPUTs + .dbg_halt_st (dbg_halt_st), // Halt/Run status from CPU + .dbg_mem_addr (dbg_mem_addr), // Debug address for rd/wr access + .dbg_mem_dout (dbg_mem_dout), // Debug unit data output + .dbg_mem_en (dbg_mem_en), // Debug unit memory enable + .dbg_mem_wr (dbg_mem_wr), // Debug unit memory write + .dmem_dout (dmem_dout), // Data Memory data output + .eu_mab (eu_mab[15:1]), // Execution Unit Memory address bus + .eu_mb_en (eu_mb_en), // Execution Unit Memory bus enable + .eu_mb_wr (eu_mb_wr), // Execution Unit Memory bus write transfer + .eu_mdb_out (eu_mdb_out), // Execution Unit Memory data bus output + .fe_mab (fe_mab[15:1]), // Frontend Memory address bus + .fe_mb_en (fe_mb_en), // Frontend Memory bus enable + .mclk (mclk), // Main system clock + .per_dout (per_dout_or), // Peripheral data output + .pmem_dout (pmem_dout), // Program Memory data output + .puc_rst (puc_rst), // Main system reset + .scan_enable (scan_enable) // Scan enable (active during scan shifting) +); + + +//============================================================================= +// 6) SPECIAL FUNCTION REGISTERS +//============================================================================= +omsp_sfr sfr_0 ( + +// OUTPUTs + .cpu_id (cpu_id), // CPU ID + .nmi_pnd (nmi_pnd), // NMI Pending + .nmi_wkup (nmi_wkup), // NMI Wakeup + .per_dout (per_dout_sfr), // Peripheral data output + .wdtie (wdtie), // Watchdog-timer interrupt enable + .wdtifg_sw_clr(wdtifg_sw_clr), // Watchdog-timer interrupt flag software clear + .wdtifg_sw_set(wdtifg_sw_set), // Watchdog-timer interrupt flag software set + +// INPUTs + .mclk (mclk), // Main system clock + .nmi (nmi), // Non-maskable interrupt (asynchronous) + .nmi_acc (nmi_acc), // Non-Maskable interrupt request accepted + .per_addr (per_addr), // Peripheral address + .per_din (per_din), // Peripheral data input + .per_en (per_en), // Peripheral enable (high active) + .per_we (per_we), // Peripheral write enable (high active) + .puc_rst (puc_rst), // Main system reset + .scan_mode (scan_mode), // Scan mode + .wdtifg (wdtifg), // Watchdog-timer interrupt flag + .wdtnmies (wdtnmies) // Watchdog-timer NMI edge selection +); + + +//============================================================================= +// 7) WATCHDOG TIMER +//============================================================================= +`ifdef WATCHDOG +omsp_watchdog watchdog_0 ( + +// OUTPUTs + .per_dout (per_dout_wdog), // Peripheral data output + .wdt_irq (wdt_irq), // Watchdog-timer interrupt + .wdt_reset (wdt_reset), // Watchdog-timer reset + .wdt_wkup (wdt_wkup), // Watchdog Wakeup + .wdtifg (wdtifg), // Watchdog-timer interrupt flag + .wdtnmies (wdtnmies), // Watchdog-timer NMI edge selection + +// INPUTs + .aclk (aclk), // ACLK + .aclk_en (aclk_en), // ACLK enable + .dbg_freeze (dbg_freeze), // Freeze Watchdog counter + .mclk (mclk), // Main system clock + .per_addr (per_addr), // Peripheral address + .per_din (per_din), // Peripheral data input + .per_en (per_en), // Peripheral enable (high active) + .per_we (per_we), // Peripheral write enable (high active) + .por (por), // Power-on reset + .puc_rst (puc_rst), // Main system reset + .scan_enable (scan_enable), // Scan enable (active during scan shifting) + .scan_mode (scan_mode), // Scan mode + .smclk (smclk), // SMCLK + .smclk_en (smclk_en), // SMCLK enable + .wdtie (wdtie), // Watchdog-timer interrupt enable + .wdtifg_irq_clr (irq_acc[10]), // Clear Watchdog-timer interrupt flag + .wdtifg_sw_clr (wdtifg_sw_clr), // Watchdog-timer interrupt flag software clear + .wdtifg_sw_set (wdtifg_sw_set) // Watchdog-timer interrupt flag software set +); +`else +assign per_dout_wdog = 16'h0000; +assign wdt_irq = 1'b0; +assign wdt_reset = 1'b0; +assign wdt_wkup = 1'b0; +assign wdtifg = 1'b0; +assign wdtnmies = 1'b0; +`endif + + +//============================================================================= +// 8) HARDWARE MULTIPLIER +//============================================================================= +`ifdef MULTIPLIER +omsp_multiplier multiplier_0 ( + +// OUTPUTs + .per_dout (per_dout_mpy), // Peripheral data output + +// INPUTs + .mclk (mclk), // Main system clock + .per_addr (per_addr), // Peripheral address + .per_din (per_din), // Peripheral data input + .per_en (per_en), // Peripheral enable (high active) + .per_we (per_we), // Peripheral write enable (high active) + .puc_rst (puc_rst), // Main system reset + .scan_enable (scan_enable) // Scan enable (active during scan shifting) +); +`else +assign per_dout_mpy = 16'h0000; +`endif + +//============================================================================= +// 9) PERIPHERALS' OUTPUT BUS +//============================================================================= + +assign per_dout_or = per_dout | + per_dout_clk | + per_dout_sfr | + per_dout_wdog | + per_dout_mpy; + + +//============================================================================= +// 10) DEBUG INTERFACE +//============================================================================= + +`ifdef DBG_EN +omsp_dbg dbg_0 ( + +// OUTPUTs + .dbg_freeze (dbg_freeze), // Freeze peripherals + .dbg_halt_cmd (dbg_halt_cmd), // Halt CPU command + .dbg_mem_addr (dbg_mem_addr), // Debug address for rd/wr access + .dbg_mem_dout (dbg_mem_dout), // Debug unit data output + .dbg_mem_en (dbg_mem_en), // Debug unit memory enable + .dbg_mem_wr (dbg_mem_wr), // Debug unit memory write + .dbg_reg_wr (dbg_reg_wr), // Debug unit CPU register write + .dbg_cpu_reset(dbg_cpu_reset), // Reset CPU from debug interface + .dbg_uart_txd (dbg_uart_txd), // Debug interface: UART TXD + +// INPUTs + .cpu_en_s (cpu_en_s), // Enable CPU code execution (synchronous) + .cpu_id (cpu_id), // CPU ID + .dbg_clk (dbg_clk), // Debug unit clock + .dbg_en_s (dbg_en_s), // Debug interface enable (synchronous) + .dbg_halt_st (dbg_halt_st), // Halt/Run status from CPU + .dbg_mem_din (dbg_mem_din), // Debug unit Memory data input + .dbg_reg_din (dbg_reg_din), // Debug unit CPU register data input + .dbg_rst (dbg_rst), // Debug unit reset + .dbg_uart_rxd (dbg_uart_rxd), // Debug interface: UART RXD (asynchronous) + .decode_noirq (decode_noirq), // Frontend decode instruction + .eu_mab (eu_mab), // Execution-Unit Memory address bus + .eu_mb_en (eu_mb_en), // Execution-Unit Memory bus enable + .eu_mb_wr (eu_mb_wr), // Execution-Unit Memory bus write transfer + .eu_mdb_in (eu_mdb_in), // Memory data bus input + .eu_mdb_out (eu_mdb_out), // Memory data bus output + .exec_done (exec_done), // Execution completed + .fe_mb_en (fe_mb_en), // Frontend Memory bus enable + .fe_mdb_in (fe_mdb_in), // Frontend Memory data bus input + .pc (pc), // Program counter + .puc_pnd_set (puc_pnd_set) // PUC pending set for the serial debug interface +); + +`else +assign dbg_freeze = ~cpu_en_s; +assign dbg_halt_cmd = 1'b0; +assign dbg_mem_addr = 16'h0000; +assign dbg_mem_dout = 16'h0000; +assign dbg_mem_en = 1'b0; +assign dbg_mem_wr = 2'b00; +assign dbg_reg_wr = 1'b0; +assign dbg_cpu_reset = 1'b0; +assign dbg_uart_txd = 1'b0; +`endif + + +endmodule // openMSP430 + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif diff --git a/tests/openmsp430/rtl/openMSP430_defines.v b/tests/openmsp430/rtl/openMSP430_defines.v new file mode 100644 index 000000000..368f7a5b1 --- /dev/null +++ b/tests/openmsp430/rtl/openMSP430_defines.v @@ -0,0 +1,843 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: openMSP430_defines.v +// +// *Module Description: +// openMSP430 Configuration file +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 151 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-07-23 00:24:11 +0200 (Mon, 23 Jul 2012) $ +//---------------------------------------------------------------------------- +//`define OMSP_NO_INCLUDE +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif + +//============================================================================ +//============================================================================ +// BASIC SYSTEM CONFIGURATION +//============================================================================ +//============================================================================ +// +// Note: the sum of program, data and peripheral memory spaces must not +// exceed 64 kB +// + +// Program Memory Size: +// Uncomment the required memory size +//------------------------------------------------------- +//`define PMEM_SIZE_CUSTOM +//`define PMEM_SIZE_59_KB +//`define PMEM_SIZE_55_KB +//`define PMEM_SIZE_54_KB +//`define PMEM_SIZE_51_KB +//`define PMEM_SIZE_48_KB +//`define PMEM_SIZE_41_KB +//`define PMEM_SIZE_32_KB +//`define PMEM_SIZE_24_KB +//`define PMEM_SIZE_16_KB +//`define PMEM_SIZE_12_KB +//`define PMEM_SIZE_8_KB +//`define PMEM_SIZE_4_KB +`define PMEM_SIZE_2_KB +//`define PMEM_SIZE_1_KB + + +// Data Memory Size: +// Uncomment the required memory size +//------------------------------------------------------- +//`define DMEM_SIZE_CUSTOM +//`define DMEM_SIZE_32_KB +//`define DMEM_SIZE_24_KB +//`define DMEM_SIZE_16_KB +//`define DMEM_SIZE_10_KB +//`define DMEM_SIZE_8_KB +//`define DMEM_SIZE_5_KB +//`define DMEM_SIZE_4_KB +//`define DMEM_SIZE_2p5_KB +//`define DMEM_SIZE_2_KB +//`define DMEM_SIZE_1_KB +//`define DMEM_SIZE_512_B +//`define DMEM_SIZE_256_B +`define DMEM_SIZE_128_B + + +// Include/Exclude Hardware Multiplier +`define MULTIPLIER + + +// Include/Exclude Serial Debug interface +`define DBG_EN + + +//============================================================================ +//============================================================================ +// ADVANCED SYSTEM CONFIGURATION (FOR EXPERIENCED USERS) +//============================================================================ +//============================================================================ + +//------------------------------------------------------- +// Custom user version number +//------------------------------------------------------- +// This 5 bit field can be freely used in order to allow +// custom identification of the system through the debug +// interface. +// (see CPU_ID.USER_VERSION field in the documentation) +//------------------------------------------------------- +`define USER_VERSION 5'b00000 + + +//------------------------------------------------------- +// Include/Exclude Watchdog timer +//------------------------------------------------------- +// When excluded, the following functionality will be +// lost: +// - Watchog (both interval and watchdog modes) +// - NMI interrupt edge selection +// - Possibility to generate a software PUC reset +//------------------------------------------------------- +`define WATCHDOG + + +///------------------------------------------------------- +// Include/Exclude Non-Maskable-Interrupt support +//------------------------------------------------------- +`define NMI + + +//------------------------------------------------------- +// Input synchronizers +//------------------------------------------------------- +// In some cases, the asynchronous input ports might +// already be synchronized externally. +// If an extensive CDC design review showed that this +// is really the case, the individual synchronizers +// can be disabled with the following defines. +// +// Notes: +// - all three signals are all sampled in the MCLK domain +// +// - the dbg_en signal reset the debug interface +// when 0. Therefore make sure it is glitch free. +// +//------------------------------------------------------- +`define SYNC_NMI +//`define SYNC_CPU_EN +//`define SYNC_DBG_EN + + +//------------------------------------------------------- +// Peripheral Memory Space: +//------------------------------------------------------- +// The original MSP430 architecture map the peripherals +// from 0x0000 to 0x01FF (i.e. 512B of the memory space). +// The following defines allow you to expand this space +// up to 32 kB (i.e. from 0x0000 to 0x7fff). +// As a consequence, the data memory mapping will be +// shifted up and a custom linker script will therefore +// be required by the GCC compiler. +//------------------------------------------------------- +//`define PER_SIZE_CUSTOM +//`define PER_SIZE_32_KB +//`define PER_SIZE_16_KB +//`define PER_SIZE_8_KB +//`define PER_SIZE_4_KB +//`define PER_SIZE_2_KB +//`define PER_SIZE_1_KB +`define PER_SIZE_512_B + + +//------------------------------------------------------- +// Defines the debugger CPU_CTL.RST_BRK_EN reset value +// (CPU break on PUC reset) +//------------------------------------------------------- +// When defined, the CPU will automatically break after +// a PUC occurrence by default. This is typically useful +// when the program memory can only be initialized through +// the serial debug interface. +//------------------------------------------------------- +`define DBG_RST_BRK_EN + + +//============================================================================ +//============================================================================ +// EXPERT SYSTEM CONFIGURATION ( !!!! EXPERTS ONLY !!!! ) +//============================================================================ +//============================================================================ +// +// IMPORTANT NOTE: Please update following configuration options ONLY if +// you have a good reason to do so... and if you know what +// you are doing :-P +// +//============================================================================ + +//------------------------------------------------------- +// Number of hardware breakpoint/watchpoint units +// (each unit contains two hardware addresses available +// for breakpoints or watchpoints): +// - DBG_HWBRK_0 -> Include hardware breakpoints unit 0 +// - DBG_HWBRK_1 -> Include hardware breakpoints unit 1 +// - DBG_HWBRK_2 -> Include hardware breakpoints unit 2 +// - DBG_HWBRK_3 -> Include hardware breakpoints unit 3 +//------------------------------------------------------- +// Please keep in mind that hardware breakpoints only +// make sense whenever the program memory is not an SRAM +// (i.e. Flash/OTP/ROM/...) or when you are interested +// in data breakpoints. +//------------------------------------------------------- +//`define DBG_HWBRK_0 +//`define DBG_HWBRK_1 +//`define DBG_HWBRK_2 +//`define DBG_HWBRK_3 + + +//------------------------------------------------------- +// Enable/Disable the hardware breakpoint RANGE mode +//------------------------------------------------------- +// When enabled this feature allows the hardware breakpoint +// units to stop the cpu whenever an instruction or data +// access lays within an address range. +// Note that this feature is not supported by GDB. +//------------------------------------------------------- +//`define DBG_HWBRK_RANGE + + +//------------------------------------------------------- +// Custom Program/Data and Peripheral Memory Spaces +//------------------------------------------------------- +// The following values are valid only if the +// corresponding *_SIZE_CUSTOM defines are uncommented: +// +// - *_SIZE : size of the section in bytes. +// - *_AWIDTH : address port width, this value must allow +// to address all WORDS of the section +// (i.e. the *_SIZE divided by 2) +//------------------------------------------------------- + +// Custom Program memory (enabled with PMEM_SIZE_CUSTOM) +`define PMEM_CUSTOM_AWIDTH 10 +`define PMEM_CUSTOM_SIZE 2028 + +// Custom Data memory (enabled with DMEM_SIZE_CUSTOM) +`define DMEM_CUSTOM_AWIDTH 6 +`define DMEM_CUSTOM_SIZE 128 + +// Custom Peripheral memory (enabled with PER_SIZE_CUSTOM) +`define PER_CUSTOM_AWIDTH 8 +`define PER_CUSTOM_SIZE 512 + + +//------------------------------------------------------- +// ASIC version +//------------------------------------------------------- +// When uncommented, this define will enable the +// ASIC system configuration section (see below) and +// will activate scan support for production test. +// +// WARNING: if you target an FPGA, leave this define +// commented. +//------------------------------------------------------- +//`define ASIC + + +//============================================================================ +//============================================================================ +// ASIC SYSTEM CONFIGURATION ( !!!! EXPERTS/PROFESSIONALS ONLY !!!! ) +//============================================================================ +//============================================================================ +`ifdef ASIC + +//=============================================================== +// FINE GRAINED CLOCK GATING +//=============================================================== + +//------------------------------------------------------- +// When uncommented, this define will enable the fine +// grained clock gating of all registers in the core. +//------------------------------------------------------- +`define CLOCK_GATING + + +//=============================================================== +// LFXT CLOCK DOMAIN +//=============================================================== + +//------------------------------------------------------- +// When uncommented, this define will enable the lfxt_clk +// clock domain. +// When commented out, the whole chip is clocked with dco_clk. +//------------------------------------------------------- +`define LFXT_DOMAIN + + +//=============================================================== +// CLOCK MUXES +//=============================================================== + +//------------------------------------------------------- +// MCLK: Clock Mux +//------------------------------------------------------- +// When uncommented, this define will enable the +// MCLK clock MUX allowing the selection between +// DCO_CLK and LFXT_CLK with the BCSCTL2.SELMx register. +// When commented, DCO_CLK is selected. +//------------------------------------------------------- +`define MCLK_MUX + +//------------------------------------------------------- +// SMCLK: Clock Mux +//------------------------------------------------------- +// When uncommented, this define will enable the +// SMCLK clock MUX allowing the selection between +// DCO_CLK and LFXT_CLK with the BCSCTL2.SELS register. +// When commented, DCO_CLK is selected. +//------------------------------------------------------- +`define SMCLK_MUX + +//------------------------------------------------------- +// WATCHDOG: Clock Mux +//------------------------------------------------------- +// When uncommented, this define will enable the +// Watchdog clock MUX allowing the selection between +// ACLK and SMCLK with the WDTCTL.WDTSSEL register. +// When commented out, ACLK is selected if the +// WATCHDOG_NOMUX_ACLK define is uncommented, SMCLK is +// selected otherwise. +//------------------------------------------------------- +`define WATCHDOG_MUX +//`define WATCHDOG_NOMUX_ACLK + + +//=============================================================== +// CLOCK DIVIDERS +//=============================================================== + +//------------------------------------------------------- +// MCLK: Clock divider +//------------------------------------------------------- +// When uncommented, this define will enable the +// MCLK clock divider (/1/2/4/8) +//------------------------------------------------------- +`define MCLK_DIVIDER + +//------------------------------------------------------- +// SMCLK: Clock divider (/1/2/4/8) +//------------------------------------------------------- +// When uncommented, this define will enable the +// SMCLK clock divider +//------------------------------------------------------- +`define SMCLK_DIVIDER + +//------------------------------------------------------- +// ACLK: Clock divider (/1/2/4/8) +//------------------------------------------------------- +// When uncommented, this define will enable the +// ACLK clock divider +//------------------------------------------------------- +`define ACLK_DIVIDER + + +//=============================================================== +// LOW POWER MODES +//=============================================================== + +//------------------------------------------------------- +// LOW POWER MODE: CPUOFF +//------------------------------------------------------- +// When uncommented, this define will include the +// clock gate allowing to switch off MCLK in +// all low power modes: LPM0, LPM1, LPM2, LPM3, LPM4 +//------------------------------------------------------- +`define CPUOFF_EN + +//------------------------------------------------------- +// LOW POWER MODE: SCG0 +//------------------------------------------------------- +// When uncommented, this define will enable the +// DCO_ENABLE/WKUP port control (always 1 when commented). +// This allows to switch off the DCO oscillator in the +// following low power modes: LPM1, LPM3, LPM4 +//------------------------------------------------------- +`define SCG0_EN + +//------------------------------------------------------- +// LOW POWER MODE: SCG1 +//------------------------------------------------------- +// When uncommented, this define will include the +// clock gate allowing to switch off SMCLK in +// the following low power modes: LPM2, LPM3, LPM4 +//------------------------------------------------------- +`define SCG1_EN + +//------------------------------------------------------- +// LOW POWER MODE: OSCOFF +//------------------------------------------------------- +// When uncommented, this define will include the +// LFXT_CLK clock gate and enable the LFXT_ENABLE/WKUP +// port control (always 1 when commented). +// This allows to switch off the low frequency oscillator +// in the following low power modes: LPM4 +//------------------------------------------------------- +`define OSCOFF_EN + + + +`endif + +//==========================================================================// +//==========================================================================// +//==========================================================================// +//==========================================================================// +//===== SYSTEM CONSTANTS --- !!!!!!!! DO NOT EDIT !!!!!!!! =====// +//==========================================================================// +//==========================================================================// +//==========================================================================// +//==========================================================================// + +// +// PROGRAM, DATA & PERIPHERAL MEMORY CONFIGURATION +//================================================== + +// Program Memory Size +`ifdef PMEM_SIZE_59_KB + `define PMEM_AWIDTH 15 + `define PMEM_SIZE 60416 +`endif +`ifdef PMEM_SIZE_55_KB + `define PMEM_AWIDTH 15 + `define PMEM_SIZE 56320 +`endif +`ifdef PMEM_SIZE_54_KB + `define PMEM_AWIDTH 15 + `define PMEM_SIZE 55296 +`endif +`ifdef PMEM_SIZE_51_KB + `define PMEM_AWIDTH 15 + `define PMEM_SIZE 52224 +`endif +`ifdef PMEM_SIZE_48_KB + `define PMEM_AWIDTH 15 + `define PMEM_SIZE 49152 +`endif +`ifdef PMEM_SIZE_41_KB + `define PMEM_AWIDTH 15 + `define PMEM_SIZE 41984 +`endif +`ifdef PMEM_SIZE_32_KB + `define PMEM_AWIDTH 14 + `define PMEM_SIZE 32768 +`endif +`ifdef PMEM_SIZE_24_KB + `define PMEM_AWIDTH 14 + `define PMEM_SIZE 24576 +`endif +`ifdef PMEM_SIZE_16_KB + `define PMEM_AWIDTH 13 + `define PMEM_SIZE 16384 +`endif +`ifdef PMEM_SIZE_12_KB + `define PMEM_AWIDTH 13 + `define PMEM_SIZE 12288 +`endif +`ifdef PMEM_SIZE_8_KB + `define PMEM_AWIDTH 12 + `define PMEM_SIZE 8192 +`endif +`ifdef PMEM_SIZE_4_KB + `define PMEM_AWIDTH 11 + `define PMEM_SIZE 4096 +`endif +`ifdef PMEM_SIZE_2_KB + `define PMEM_AWIDTH 10 + `define PMEM_SIZE 2048 +`endif +`ifdef PMEM_SIZE_1_KB + `define PMEM_AWIDTH 9 + `define PMEM_SIZE 1024 +`endif +`ifdef PMEM_SIZE_CUSTOM + `define PMEM_AWIDTH `PMEM_CUSTOM_AWIDTH + `define PMEM_SIZE `PMEM_CUSTOM_SIZE +`endif + +// Data Memory Size +`ifdef DMEM_SIZE_32_KB + `define DMEM_AWIDTH 14 + `define DMEM_SIZE 32768 +`endif +`ifdef DMEM_SIZE_24_KB + `define DMEM_AWIDTH 14 + `define DMEM_SIZE 24576 +`endif +`ifdef DMEM_SIZE_16_KB + `define DMEM_AWIDTH 13 + `define DMEM_SIZE 16384 +`endif +`ifdef DMEM_SIZE_10_KB + `define DMEM_AWIDTH 13 + `define DMEM_SIZE 10240 +`endif +`ifdef DMEM_SIZE_8_KB + `define DMEM_AWIDTH 12 + `define DMEM_SIZE 8192 +`endif +`ifdef DMEM_SIZE_5_KB + `define DMEM_AWIDTH 12 + `define DMEM_SIZE 5120 +`endif +`ifdef DMEM_SIZE_4_KB + `define DMEM_AWIDTH 11 + `define DMEM_SIZE 4096 +`endif +`ifdef DMEM_SIZE_2p5_KB + `define DMEM_AWIDTH 11 + `define DMEM_SIZE 2560 +`endif +`ifdef DMEM_SIZE_2_KB + `define DMEM_AWIDTH 10 + `define DMEM_SIZE 2048 +`endif +`ifdef DMEM_SIZE_1_KB + `define DMEM_AWIDTH 9 + `define DMEM_SIZE 1024 +`endif +`ifdef DMEM_SIZE_512_B + `define DMEM_AWIDTH 8 + `define DMEM_SIZE 512 +`endif +`ifdef DMEM_SIZE_256_B + `define DMEM_AWIDTH 7 + `define DMEM_SIZE 256 +`endif +`ifdef DMEM_SIZE_128_B + `define DMEM_AWIDTH 6 + `define DMEM_SIZE 128 +`endif +`ifdef DMEM_SIZE_CUSTOM + `define DMEM_AWIDTH `DMEM_CUSTOM_AWIDTH + `define DMEM_SIZE `DMEM_CUSTOM_SIZE +`endif + +// Peripheral Memory Size +`ifdef PER_SIZE_32_KB + `define PER_AWIDTH 14 + `define PER_SIZE 32768 +`endif +`ifdef PER_SIZE_16_KB + `define PER_AWIDTH 13 + `define PER_SIZE 16384 +`endif +`ifdef PER_SIZE_8_KB + `define PER_AWIDTH 12 + `define PER_SIZE 8192 +`endif +`ifdef PER_SIZE_4_KB + `define PER_AWIDTH 11 + `define PER_SIZE 4096 +`endif +`ifdef PER_SIZE_2_KB + `define PER_AWIDTH 10 + `define PER_SIZE 2048 +`endif +`ifdef PER_SIZE_1_KB + `define PER_AWIDTH 9 + `define PER_SIZE 1024 +`endif +`ifdef PER_SIZE_512_B + `define PER_AWIDTH 8 + `define PER_SIZE 512 +`endif +`ifdef PER_SIZE_CUSTOM + `define PER_AWIDTH `PER_CUSTOM_AWIDTH + `define PER_SIZE `PER_CUSTOM_SIZE +`endif + +// Data Memory Base Adresses +`define DMEM_BASE `PER_SIZE + +// Program & Data Memory most significant address bit (for 16 bit words) +`define PMEM_MSB `PMEM_AWIDTH-1 +`define DMEM_MSB `DMEM_AWIDTH-1 +`define PER_MSB `PER_AWIDTH-1 + +// +// STATES, REGISTER FIELDS, ... +//====================================== + +// Instructions type +`define INST_SO 0 +`define INST_JMP 1 +`define INST_TO 2 + +// Single-operand arithmetic +`define RRC 0 +`define SWPB 1 +`define RRA 2 +`define SXT 3 +`define PUSH 4 +`define CALL 5 +`define RETI 6 +`define IRQ 7 + +// Conditional jump +`define JNE 0 +`define JEQ 1 +`define JNC 2 +`define JC 3 +`define JN 4 +`define JGE 5 +`define JL 6 +`define JMP 7 + +// Two-operand arithmetic +`define MOV 0 +`define ADD 1 +`define ADDC 2 +`define SUBC 3 +`define SUB 4 +`define CMP 5 +`define DADD 6 +`define BIT 7 +`define BIC 8 +`define BIS 9 +`define XOR 10 +`define AND 11 + +// Addressing modes +`define DIR 0 +`define IDX 1 +`define INDIR 2 +`define INDIR_I 3 +`define SYMB 4 +`define IMM 5 +`define ABS 6 +`define CONST 7 + +// Instruction state machine +`define I_IRQ_FETCH 3'h0 +`define I_IRQ_DONE 3'h1 +`define I_DEC 3'h2 +`define I_EXT1 3'h3 +`define I_EXT2 3'h4 +`define I_IDLE 3'h5 + +// Execution state machine +// (swapped E_IRQ_0 and E_IRQ_2 values to suppress glitch generation warning from lint tool) +`define E_IRQ_0 4'h2 +`define E_IRQ_1 4'h1 +`define E_IRQ_2 4'h0 +`define E_IRQ_3 4'h3 +`define E_IRQ_4 4'h4 +`define E_SRC_AD 4'h5 +`define E_SRC_RD 4'h6 +`define E_SRC_WR 4'h7 +`define E_DST_AD 4'h8 +`define E_DST_RD 4'h9 +`define E_DST_WR 4'hA +`define E_EXEC 4'hB +`define E_JUMP 4'hC +`define E_IDLE 4'hD + +// ALU control signals +`define ALU_SRC_INV 0 +`define ALU_INC 1 +`define ALU_INC_C 2 +`define ALU_ADD 3 +`define ALU_AND 4 +`define ALU_OR 5 +`define ALU_XOR 6 +`define ALU_DADD 7 +`define ALU_STAT_7 8 +`define ALU_STAT_F 9 +`define ALU_SHIFT 10 +`define EXEC_NO_WR 11 + +// Debug interface +`define DBG_UART_WR 18 +`define DBG_UART_BW 17 +`define DBG_UART_ADDR 16:11 + +// Debug interface CPU_CTL register +`define HALT 0 +`define RUN 1 +`define ISTEP 2 +`define SW_BRK_EN 3 +`define FRZ_BRK_EN 4 +`define RST_BRK_EN 5 +`define CPU_RST 6 + +// Debug interface CPU_STAT register +`define HALT_RUN 0 +`define PUC_PND 1 +`define SWBRK_PND 3 +`define HWBRK0_PND 4 +`define HWBRK1_PND 5 + +// Debug interface BRKx_CTL register +`define BRK_MODE_RD 0 +`define BRK_MODE_WR 1 +`define BRK_MODE 1:0 +`define BRK_EN 2 +`define BRK_I_EN 3 +`define BRK_RANGE 4 + +// Basic clock module: BCSCTL1 Control Register +`define DIVAx 5:4 + +// Basic clock module: BCSCTL2 Control Register +`define SELMx 7 +`define DIVMx 5:4 +`define SELS 3 +`define DIVSx 2:1 + +// MCLK Clock gate +`ifdef CPUOFF_EN + `define MCLK_CGATE +`else +`ifdef MCLK_DIVIDER + `define MCLK_CGATE +`endif +`endif + +// SMCLK Clock gate +`ifdef SCG1_EN + `define SMCLK_CGATE +`else +`ifdef SMCLK_DIVIDER + `define SMCLK_CGATE +`endif +`endif + +// +// DEBUG INTERFACE EXTRA CONFIGURATION +//====================================== + +// Debug interface: CPU version +`define CPU_VERSION 3'h2 + +// Debug interface: Software breakpoint opcode +`define DBG_SWBRK_OP 16'h4343 + +// Debug UART interface auto data synchronization +// If the following define is commented out, then +// the DBG_UART_BAUD and DBG_DCO_FREQ need to be properly +// defined. +`define DBG_UART_AUTO_SYNC + +// Debug UART interface data rate +// In order to properly setup the UART debug interface, you +// need to specify the DCO_CLK frequency (DBG_DCO_FREQ) and +// the chosen BAUD rate from the UART interface. +// +//`define DBG_UART_BAUD 9600 +//`define DBG_UART_BAUD 19200 +//`define DBG_UART_BAUD 38400 +//`define DBG_UART_BAUD 57600 +//`define DBG_UART_BAUD 115200 +//`define DBG_UART_BAUD 230400 +//`define DBG_UART_BAUD 460800 +//`define DBG_UART_BAUD 576000 +//`define DBG_UART_BAUD 921600 +`define DBG_UART_BAUD 2000000 +`define DBG_DCO_FREQ 20000000 +`define DBG_UART_CNT ((`DBG_DCO_FREQ/`DBG_UART_BAUD)-1) + +// Debug interface selection +// `define DBG_UART -> Enable UART (8N1) debug interface +// `define DBG_JTAG -> DON'T UNCOMMENT, NOT SUPPORTED +// +`define DBG_UART +//`define DBG_JTAG + +// Debug interface input synchronizer +`define SYNC_DBG_UART_RXD + +// Enable/Disable the hardware breakpoint RANGE mode +`ifdef DBG_HWBRK_RANGE + `define HWBRK_RANGE 1'b1 +`else + `define HWBRK_RANGE 1'b0 +`endif + +// Counter width for the debug interface UART +`define DBG_UART_XFER_CNT_W 16 + +// Check configuration +`ifdef DBG_EN + `ifdef DBG_UART + `ifdef DBG_JTAG +CONFIGURATION ERROR: JTAG AND UART DEBUG INTERFACE ARE BOTH ENABLED + `endif + `else + `ifdef DBG_JTAG +CONFIGURATION ERROR: JTAG INTERFACE NOT SUPPORTED + `else +CONFIGURATION ERROR: JTAG OR UART DEBUG INTERFACE SHOULD BE ENABLED + `endif + `endif +`endif + +// +// MULTIPLIER CONFIGURATION +//====================================== + +// If uncommented, the following define selects +// the 16x16 multiplier (1 cycle) instead of the +// default 16x8 multplier (2 cycles) +//`define MPY_16x16 + +//====================================== +// CONFIGURATION CHECKS +//====================================== +`ifdef LFXT_DOMAIN +`else + `ifdef MCLK_MUX +CONFIGURATION ERROR: THE MCLK_MUX CAN ONLY BE ENABLED IF THE LFXT_DOMAIN IS ENABLED AS WELL + `endif + `ifdef SMCLK_MUX +CONFIGURATION ERROR: THE SMCLK_MUX CAN ONLY BE ENABLED IF THE LFXT_DOMAIN IS ENABLED AS WELL + `endif + `ifdef WATCHDOG_MUX +CONFIGURATION ERROR: THE WATCHDOG_MUX CAN ONLY BE ENABLED IF THE LFXT_DOMAIN IS ENABLED AS WELL + `else + `ifdef WATCHDOG_NOMUX_ACLK +CONFIGURATION ERROR: THE WATCHDOG_NOMUX_ACLK CAN ONLY BE ENABLED IF THE LFXT_DOMAIN IS ENABLED AS WELL + `endif + `endif + `ifdef OSCOFF_EN +CONFIGURATION ERROR: THE OSCOFF LOW POWER MODE CAN ONLY BE ENABLED IF THE LFXT_DOMAIN IS ENABLED AS WELL + `endif +`endif diff --git a/tests/openmsp430/rtl/openMSP430_undefines.v b/tests/openmsp430/rtl/openMSP430_undefines.v new file mode 100644 index 000000000..a399ae779 --- /dev/null +++ b/tests/openmsp430/rtl/openMSP430_undefines.v @@ -0,0 +1,732 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: openMSP430_undefines.v +// +// *Module Description: +// openMSP430 Verilog `undef file +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 23 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2009-08-30 18:39:26 +0200 (Sun, 30 Aug 2009) $ +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// BASIC SYSTEM CONFIGURATION +//---------------------------------------------------------------------------- + +// Program Memory sizes +`ifdef PMEM_SIZE_59_KB +`undef PMEM_SIZE_59_KB +`endif +`ifdef PMEM_SIZE_55_KB +`undef PMEM_SIZE_55_KB +`endif +`ifdef PMEM_SIZE_54_KB +`undef PMEM_SIZE_54_KB +`endif +`ifdef PMEM_SIZE_51_KB +`undef PMEM_SIZE_51_KB +`endif +`ifdef PMEM_SIZE_48_KB +`undef PMEM_SIZE_48_KB +`endif +`ifdef PMEM_SIZE_41_KB +`undef PMEM_SIZE_41_KB +`endif +`ifdef PMEM_SIZE_32_KB +`undef PMEM_SIZE_32_KB +`endif +`ifdef PMEM_SIZE_24_KB +`undef PMEM_SIZE_24_KB +`endif +`ifdef PMEM_SIZE_16_KB +`undef PMEM_SIZE_16_KB +`endif +`ifdef PMEM_SIZE_12_KB +`undef PMEM_SIZE_12_KB +`endif +`ifdef PMEM_SIZE_8_KB +`undef PMEM_SIZE_8_KB +`endif +`ifdef PMEM_SIZE_4_KB +`undef PMEM_SIZE_4_KB +`endif +`ifdef PMEM_SIZE_2_KB +`undef PMEM_SIZE_2_KB +`endif +`ifdef PMEM_SIZE_1_KB +`undef PMEM_SIZE_1_KB +`endif + +// Data Memory sizes +`ifdef DMEM_SIZE_32_KB +`undef DMEM_SIZE_32_KB +`endif +`ifdef DMEM_SIZE_24_KB +`undef DMEM_SIZE_24_KB +`endif +`ifdef DMEM_SIZE_16_KB +`undef DMEM_SIZE_16_KB +`endif +`ifdef DMEM_SIZE_10_KB +`undef DMEM_SIZE_10_KB +`endif +`ifdef DMEM_SIZE_8_KB +`undef DMEM_SIZE_8_KB +`endif +`ifdef DMEM_SIZE_5_KB +`undef DMEM_SIZE_5_KB +`endif +`ifdef DMEM_SIZE_4_KB +`undef DMEM_SIZE_4_KB +`endif +`ifdef DMEM_SIZE_2p5_KB +`undef DMEM_SIZE_2p5_KB +`endif +`ifdef DMEM_SIZE_2_KB +`undef DMEM_SIZE_2_KB +`endif +`ifdef DMEM_SIZE_1_KB +`undef DMEM_SIZE_1_KB +`endif +`ifdef DMEM_SIZE_512_B +`undef DMEM_SIZE_512_B +`endif +`ifdef DMEM_SIZE_256_B +`undef DMEM_SIZE_256_B +`endif +`ifdef DMEM_SIZE_128_B +`undef DMEM_SIZE_128_B +`endif + +// Include/Exclude Hardware Multiplier +`ifdef MULTIPLIER +`undef MULTIPLIER +`endif + +// Include Debug interface +`ifdef DBG_EN +`undef DBG_EN +`endif + + +//---------------------------------------------------------------------------- +// ADVANCED SYSTEM CONFIGURATION (FOR EXPERIENCED USERS) +//---------------------------------------------------------------------------- + +// Peripheral Memory Space: +`ifdef PER_SIZE_32_KB +`undef PER_SIZE_32_KB +`endif +`ifdef PER_SIZE_16_KB +`undef PER_SIZE_16_KB +`endif +`ifdef PER_SIZE_8_KB +`undef PER_SIZE_8_KB +`endif +`ifdef PER_SIZE_4_KB +`undef PER_SIZE_4_KB +`endif +`ifdef PER_SIZE_2_KB +`undef PER_SIZE_2_KB +`endif +`ifdef PER_SIZE_1_KB +`undef PER_SIZE_1_KB +`endif +`ifdef PER_SIZE_512_B +`undef PER_SIZE_512_B +`endif + +// Let the CPU break after a PUC occurrence by default +`ifdef DBG_RST_BRK_EN +`undef DBG_RST_BRK_EN +`endif + +// Custom user version number +`ifdef USER_VERSION +`undef USER_VERSION +`endif + +// Include/Exclude Watchdog timer +`ifdef WATCHDOG +`undef WATCHDOG +`endif + +// Include/Exclude Non-Maskable-Interrupt support +`ifdef NMI +`undef NMI +`endif + +//---------------------------------------------------------------------------- +// EXPERT SYSTEM CONFIGURATION ( !!!! EXPERTS ONLY !!!! ) +//---------------------------------------------------------------------------- + +// Number of hardware breakpoint units +`ifdef DBG_HWBRK_0 +`undef DBG_HWBRK_0 +`endif +`ifdef DBG_HWBRK_1 +`undef DBG_HWBRK_1 +`endif +`ifdef DBG_HWBRK_2 +`undef DBG_HWBRK_2 +`endif +`ifdef DBG_HWBRK_3 +`undef DBG_HWBRK_3 +`endif + +// Enable/Disable the hardware breakpoint RANGE mode +`ifdef DBG_HWBRK_RANGE +`undef DBG_HWBRK_RANGE +`endif + +// Input synchronizers +`ifdef SYNC_CPU_EN +`undef SYNC_CPU_EN +`endif +`ifdef SYNC_DBG_EN +`undef SYNC_DBG_EN +`endif +`ifdef SYNC_DBG_UART_RXD +`undef SYNC_DBG_UART_RXD +`endif +`ifdef SYNC_NMI +`undef SYNC_NMI +`endif + +// ASIC version +`ifdef ASIC +`undef ASIC +`endif + + +//---------------------------------------------------------------------------- +// ASIC SYSTEM CONFIGURATION ( !!!! EXPERTS ONLY !!!! ) +//---------------------------------------------------------------------------- + +// Fine grained clock gating +`ifdef CLOCK_GATING +`undef CLOCK_GATING +`endif + +// LFXT clock domain +`ifdef LFXT_DOMAIN +`undef LFXT_DOMAIN +`endif + +// MCLK: Clock Mux +`ifdef MCLK_MUX +`undef MCLK_MUX +`endif + +// SMCLK: Clock Mux +`ifdef SMCLK_MUX +`undef SMCLK_MUX +`endif + +// WATCHDOG: Clock Mux +`ifdef WATCHDOG_MUX +`undef WATCHDOG_MUX +`endif + +// MCLK: Clock divider +`ifdef MCLK_DIVIDER +`undef MCLK_DIVIDER +`endif + +// SMCLK: Clock divider (/1/2/4/8) +`ifdef SMCLK_DIVIDER +`undef SMCLK_DIVIDER +`endif + +// ACLK: Clock divider (/1/2/4/8) +`ifdef ACLK_DIVIDER +`undef ACLK_DIVIDER +`endif + +// LOW POWER MODE: CPUOFF +`ifdef CPUOFF_EN +`undef CPUOFF_EN +`endif + +// LOW POWER MODE: OSCOFF +`ifdef OSCOFF_EN +`undef OSCOFF_EN +`endif + +// LOW POWER MODE: SCG0 +`ifdef SCG0_EN +`undef SCG0_EN +`endif + +// LOW POWER MODE: SCG1 +`ifdef SCG1_EN +`undef SCG1_EN +`endif + + +//==========================================================================// +//==========================================================================// +//==========================================================================// +//==========================================================================// +//===== SYSTEM CONSTANTS --- !!!!!!!! DO NOT EDIT !!!!!!!! =====// +//==========================================================================// +//==========================================================================// +//==========================================================================// +//==========================================================================// + +// Program Memory Size +`ifdef PMEM_AWIDTH +`undef PMEM_AWIDTH +`endif +`ifdef PMEM_SIZE +`undef PMEM_SIZE +`endif + +// Data Memory Size +`ifdef DMEM_AWIDTH +`undef DMEM_AWIDTH +`endif +`ifdef DMEM_SIZE +`undef DMEM_SIZE +`endif + +// Peripheral Memory Size +`ifdef PER_AWIDTH +`undef PER_AWIDTH +`endif +`ifdef PER_SIZE +`undef PER_SIZE +`endif + +// Data Memory Base Adresses +`ifdef DMEM_BASE +`undef DMEM_BASE +`endif + +// Program & Data Memory most significant address bit (for 16 bit words) +`ifdef PMEM_MSB +`undef PMEM_MSB +`endif +`ifdef DMEM_MSB +`undef DMEM_MSB +`endif +`ifdef PER_MSB +`undef PER_MSB +`endif + +// Instructions type +`ifdef INST_SO +`undef INST_SO +`endif +`ifdef INST_JMP +`undef INST_JMP +`endif +`ifdef INST_TO +`undef INST_TO +`endif + +// Single-operand arithmetic +`ifdef RRC +`undef RRC +`endif +`ifdef SWPB +`undef SWPB +`endif +`ifdef RRA +`undef RRA +`endif +`ifdef SXT +`undef SXT +`endif +`ifdef PUSH +`undef PUSH +`endif +`ifdef CALL +`undef CALL +`endif +`ifdef RETI +`undef RETI +`endif +`ifdef IRQ +`undef IRQ +`endif + +// Conditional jump +`ifdef JNE +`undef JNE +`endif +`ifdef JEQ +`undef JEQ +`endif +`ifdef JNC +`undef JNC +`endif +`ifdef JC +`undef JC +`endif +`ifdef JN +`undef JN +`endif +`ifdef JGE +`undef JGE +`endif +`ifdef JL +`undef JL +`endif +`ifdef JMP +`undef JMP +`endif + +// Two-operand arithmetic +`ifdef MOV +`undef MOV +`endif +`ifdef ADD +`undef ADD +`endif +`ifdef ADDC +`undef ADDC +`endif +`ifdef SUBC +`undef SUBC +`endif +`ifdef SUB +`undef SUB +`endif +`ifdef CMP +`undef CMP +`endif +`ifdef DADD +`undef DADD +`endif +`ifdef BIT +`undef BIT +`endif +`ifdef BIC +`undef BIC +`endif +`ifdef BIS +`undef BIS +`endif +`ifdef XOR +`undef XOR +`endif +`ifdef AND +`undef AND +`endif + +// Addressing modes +`ifdef DIR +`undef DIR +`endif +`ifdef IDX +`undef IDX +`endif +`ifdef INDIR +`undef INDIR +`endif +`ifdef INDIR_I +`undef INDIR_I +`endif +`ifdef SYMB +`undef SYMB +`endif +`ifdef IMM +`undef IMM +`endif +`ifdef ABS +`undef ABS +`endif +`ifdef CONST +`undef CONST +`endif + +// Instruction state machine +`ifdef I_IRQ_FETCH +`undef I_IRQ_FETCH +`endif +`ifdef I_IRQ_DONE +`undef I_IRQ_DONE +`endif +`ifdef I_DEC +`undef I_DEC +`endif +`ifdef I_EXT1 +`undef I_EXT1 +`endif +`ifdef I_EXT2 +`undef I_EXT2 +`endif +`ifdef I_IDLE +`undef I_IDLE +`endif + +// Execution state machine +`ifdef E_IRQ_0 +`undef E_IRQ_0 +`endif +`ifdef E_IRQ_1 +`undef E_IRQ_1 +`endif +`ifdef E_IRQ_2 +`undef E_IRQ_2 +`endif +`ifdef E_IRQ_3 +`undef E_IRQ_3 +`endif +`ifdef E_IRQ_4 +`undef E_IRQ_4 +`endif +`ifdef E_SRC_AD +`undef E_SRC_AD +`endif +`ifdef E_SRC_RD +`undef E_SRC_RD +`endif +`ifdef E_SRC_WR +`undef E_SRC_WR +`endif +`ifdef E_DST_AD +`undef E_DST_AD +`endif +`ifdef E_DST_RD +`undef E_DST_RD +`endif +`ifdef E_DST_WR +`undef E_DST_WR +`endif +`ifdef E_EXEC +`undef E_EXEC +`endif +`ifdef E_JUMP +`undef E_JUMP +`endif +`ifdef E_IDLE +`undef E_IDLE +`endif + +// ALU control signals +`ifdef ALU_SRC_INV +`undef ALU_SRC_INV +`endif +`ifdef ALU_INC +`undef ALU_INC +`endif +`ifdef ALU_INC_C +`undef ALU_INC_C +`endif +`ifdef ALU_ADD +`undef ALU_ADD +`endif +`ifdef ALU_AND +`undef ALU_AND +`endif +`ifdef ALU_OR +`undef ALU_OR +`endif +`ifdef ALU_XOR +`undef ALU_XOR +`endif +`ifdef ALU_DADD +`undef ALU_DADD +`endif +`ifdef ALU_STAT_7 +`undef ALU_STAT_7 +`endif +`ifdef ALU_STAT_F +`undef ALU_STAT_F +`endif +`ifdef ALU_SHIFT +`undef ALU_SHIFT +`endif +`ifdef EXEC_NO_WR +`undef EXEC_NO_WR +`endif + +// Debug interface +`ifdef DBG_UART_WR +`undef DBG_UART_WR +`endif +`ifdef DBG_UART_BW +`undef DBG_UART_BW +`endif +`ifdef DBG_UART_ADDR +`undef DBG_UART_ADDR +`endif + +// Debug interface CPU_CTL register +`ifdef HALT +`undef HALT +`endif +`ifdef RUN +`undef RUN +`endif +`ifdef ISTEP +`undef ISTEP +`endif +`ifdef SW_BRK_EN +`undef SW_BRK_EN +`endif +`ifdef FRZ_BRK_EN +`undef FRZ_BRK_EN +`endif +`ifdef RST_BRK_EN +`undef RST_BRK_EN +`endif +`ifdef CPU_RST +`undef CPU_RST +`endif + +// Debug interface CPU_STAT register +`ifdef HALT_RUN +`undef HALT_RUN +`endif +`ifdef PUC_PND +`undef PUC_PND +`endif +`ifdef SWBRK_PND +`undef SWBRK_PND +`endif +`ifdef HWBRK0_PND +`undef HWBRK0_PND +`endif +`ifdef HWBRK1_PND +`undef HWBRK1_PND +`endif + +// Debug interface BRKx_CTL register +`ifdef BRK_MODE_RD +`undef BRK_MODE_RD +`endif +`ifdef BRK_MODE_WR +`undef BRK_MODE_WR +`endif +`ifdef BRK_MODE +`undef BRK_MODE +`endif +`ifdef BRK_EN +`undef BRK_EN +`endif +`ifdef BRK_I_EN +`undef BRK_I_EN +`endif +`ifdef BRK_RANGE +`undef BRK_RANGE +`endif + +// Basic clock module: BCSCTL1 Control Register +`ifdef DIVAx +`undef DIVAx +`endif + +// Basic clock module: BCSCTL2 Control Register +`ifdef SELMx +`undef SELMx +`endif +`ifdef DIVMx +`undef DIVMx +`endif +`ifdef SELS +`undef SELS +`endif +`ifdef DIVSx +`undef DIVSx +`endif + +// MCLK Clock gate +`ifdef MCLK_CGATE +`undef MCLK_CGATE +`endif + +// SMCLK Clock gate +`ifdef SMCLK_CGATE +`undef SMCLK_CGATE +`endif + +// +// DEBUG INTERFACE EXTRA CONFIGURATION +//====================================== + +// Debug interface: CPU version +`ifdef CPU_VERSION +`undef CPU_VERSION +`endif + +// Debug interface: Software breakpoint opcode +`ifdef DBG_SWBRK_OP +`undef DBG_SWBRK_OP +`endif + +// Debug UART interface auto data synchronization +`ifdef DBG_UART_AUTO_SYNC +`undef DBG_UART_AUTO_SYNC +`endif + +// Debug UART interface data rate +`ifdef DBG_UART_BAUD +`undef DBG_UART_BAUD +`endif +`ifdef DBG_DCO_FREQ +`undef DBG_DCO_FREQ +`endif +`ifdef DBG_UART_CNT +`undef DBG_UART_CNT +`endif + +// Debug interface selection +`ifdef DBG_UART +`undef DBG_UART +`endif +`ifdef DBG_JTAG +`undef DBG_JTAG +`endif + +// Enable/Disable the hardware breakpoint RANGE mode +`ifdef HWBRK_RANGE +`undef HWBRK_RANGE +`endif + +// Counter width for the debug interface UART +`ifdef DBG_UART_XFER_CNT_W +`undef DBG_UART_XFER_CNT_W +`endif + +// +// MULTIPLIER CONFIGURATION +//====================================== + +`ifdef MPY_16x16 +`undef MPY_16x16 +`endif diff --git a/tests/openmsp430/run-fm.do b/tests/openmsp430/run-fm.do new file mode 100644 index 000000000..766d974c3 --- /dev/null +++ b/tests/openmsp430/run-fm.do @@ -0,0 +1,37 @@ + +set hdlin_ignore_full_case false +set hdlin_ignore_parallel_case false +set svf_ignore_unqualified_fsm_information true +set hdlin_warn_on_mismatch_message "FMR_ELAB-115 FMR_VLOG-079 FMR_VLOG-091" + +read_verilog -container r -libname WORK -01 rtl/omsp_alu.v +read_verilog -container r -libname WORK -01 rtl/omsp_and_gate.v +read_verilog -container r -libname WORK -01 rtl/omsp_clock_gate.v +read_verilog -container r -libname WORK -01 rtl/omsp_clock_module.v +read_verilog -container r -libname WORK -01 rtl/omsp_clock_mux.v +read_verilog -container r -libname WORK -01 rtl/omsp_dbg_hwbrk.v +read_verilog -container r -libname WORK -01 rtl/omsp_dbg_uart.v +read_verilog -container r -libname WORK -01 rtl/omsp_dbg.v +read_verilog -container r -libname WORK -01 rtl/omsp_execution_unit.v +read_verilog -container r -libname WORK -01 rtl/omsp_frontend.v +read_verilog -container r -libname WORK -01 rtl/omsp_mem_backbone.v +read_verilog -container r -libname WORK -01 rtl/omsp_multiplier.v +read_verilog -container r -libname WORK -01 rtl/omsp_register_file.v +read_verilog -container r -libname WORK -01 rtl/omsp_scan_mux.v +read_verilog -container r -libname WORK -01 rtl/omsp_sfr.v +read_verilog -container r -libname WORK -01 rtl/omsp_sync_cell.v +read_verilog -container r -libname WORK -01 rtl/omsp_sync_reset.v +read_verilog -container r -libname WORK -01 rtl/omsp_wakeup_cell.v +read_verilog -container r -libname WORK -01 rtl/omsp_watchdog.v +read_verilog -container r -libname WORK -01 rtl/openMSP430.v +set_top r:/WORK/openMSP430 + +read_verilog -container i -libname WORK -01 synth.v +read_verilog -container i -technology_library -libname TECH_WORK -01 ../../techlibs/stdcells_sim.v +read_verilog -container i -technology_library -libname TECH_WORK -01 sim_mul.v +set_top i:/WORK/openMSP430 + +source fsm_info.txt + +if ![verify] start_gui exit + diff --git a/tests/openmsp430/run-fm.sh b/tests/openmsp430/run-fm.sh new file mode 100644 index 000000000..5fd1c6cf8 --- /dev/null +++ b/tests/openmsp430/run-fm.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if [ -n "$REMOTE_YOSYS_ROOT" ]; then + rsync --exclude=".svn" --exclude="*.log" -rv -e "${REMOTE_YOSYS_SSH:-ssh} -C" "$REMOTE_YOSYS_ROOT"/tests/openmsp430/. . +fi +fm_shell -64 -file run-fm.do 2>&1 | tee run-fm.log diff --git a/tests/openmsp430/run-synth.sh b/tests/openmsp430/run-synth.sh new file mode 100644 index 000000000..e4d6bc720 --- /dev/null +++ b/tests/openmsp430/run-synth.sh @@ -0,0 +1,3 @@ +#!/bin/bash +time ../../yosys -b "verilog -noexpr" -o synth.v -tl synth.log -s run-synth.ys \ + rtl/omsp_*.v rtl/openMSP430.v 2>&1 | egrep '^\[[0-9.]+\] (ERROR|-- |[0-9]+\.)' diff --git a/tests/openmsp430/run-synth.ys b/tests/openmsp430/run-synth.ys new file mode 100644 index 000000000..af39147fb --- /dev/null +++ b/tests/openmsp430/run-synth.ys @@ -0,0 +1,11 @@ +hierarchy -check -top openMSP430 +proc +opt +memory +opt +fsm -fm_set_fsm_file fsm_info.txt +opt +techmap +opt +abc +opt diff --git a/tests/openmsp430/sim_mul.v b/tests/openmsp430/sim_mul.v new file mode 100644 index 000000000..8762bb25f --- /dev/null +++ b/tests/openmsp430/sim_mul.v @@ -0,0 +1,29 @@ + +module \$mul (A, B, Y); + +parameter A_SIGNED = 0; +parameter B_SIGNED = 0; +parameter A_WIDTH = 0; +parameter B_WIDTH = 0; +parameter Y_WIDTH = 0; + +input [A_WIDTH-1:0] A; +generate if (A_SIGNED) begin:A_BUF + wire signed [A_WIDTH-1:0] val = A; +end else begin:A_BUF + wire [A_WIDTH-1:0] val = A; +end endgenerate + +input [B_WIDTH-1:0] B; +generate if (B_SIGNED) begin:B_BUF + wire signed [B_WIDTH-1:0] val = B; +end else begin:B_BUF + wire [B_WIDTH-1:0] val = B; +end endgenerate + +output [Y_WIDTH-1:0] Y; + +assign Y = A_BUF.val * B_BUF.val; + +endmodule + diff --git a/tests/or1200/config.patch b/tests/or1200/config.patch new file mode 100644 index 000000000..7826edeb6 --- /dev/null +++ b/tests/or1200/config.patch @@ -0,0 +1,46 @@ +Index: or1200_defines.v +=================================================================== +--- or1200_defines.v (revision 812) ++++ or1200_defines.v (working copy) +@@ -56,7 +56,7 @@ + // + //`define OR1200_VERBOSE + +-// `define OR1200_ASIC ++`define OR1200_ASIC + //////////////////////////////////////////////////////// + // + // Typical configuration for an ASIC +@@ -69,7 +69,7 @@ + //`define OR1200_ARTISAN_SSP + //`define OR1200_ARTISAN_SDP + //`define OR1200_ARTISAN_STP +-`define OR1200_VIRTUALSILICON_SSP ++//`define OR1200_VIRTUALSILICON_SSP + //`define OR1200_VIRTUALSILICON_STP_T1 + //`define OR1200_VIRTUALSILICON_STP_T2 + +@@ -96,17 +96,17 @@ + // + // Select between ASIC optimized and generic multiplier + // +-//`define OR1200_ASIC_MULTP2_32X32 +-`define OR1200_GENERIC_MULTP2_32X32 ++`define OR1200_ASIC_MULTP2_32X32 ++//`define OR1200_GENERIC_MULTP2_32X32 + + // + // Size/type of insn/data cache if implemented + // +-// `define OR1200_IC_1W_512B ++`define OR1200_IC_1W_512B + // `define OR1200_IC_1W_4KB +-`define OR1200_IC_1W_8KB +-// `define OR1200_DC_1W_4KB +-`define OR1200_DC_1W_8KB ++// `define OR1200_IC_1W_8KB ++`define OR1200_DC_1W_4KB ++// `define OR1200_DC_1W_8KB + + `else + diff --git a/tests/or1200/run-checkout.sh b/tests/or1200/run-checkout.sh new file mode 100644 index 000000000..c9d4a1024 --- /dev/null +++ b/tests/or1200/run-checkout.sh @@ -0,0 +1,4 @@ +#!/bin/bash +rm -rf rtl +svn co http://opencores.org/ocsvn/openrisc/openrisc/trunk/or1200/rtl/verilog rtl +( cd rtl; patch -p0 < ../config.patch; ) diff --git a/tests/or1200/run-fm-mods.sh b/tests/or1200/run-fm-mods.sh new file mode 100644 index 000000000..6b8487730 --- /dev/null +++ b/tests/or1200/run-fm-mods.sh @@ -0,0 +1,24 @@ +#!/bin/bash +if [ -n "$REMOTE_YOSYS_ROOT" ]; then + rsync --exclude=".svn" --exclude="*.log" -rv -e "${REMOTE_YOSYS_SSH:-ssh} -C" "$REMOTE_YOSYS_ROOT"/tests/or1200/. . +fi +for mod in $( grep '^module or1200_' synth.v | awk -F '[ (]' '{ print $2; }'; ) +do + { + grep '^set ' run-fm.do + grep '^read_verilog -container r ' run-fm.do + echo "set_top r:/WORK/$mod" + grep '^read_verilog -container i ' run-fm.do + echo "set_top i:/WORK/$mod" + echo "verify" + echo "exit" + } > run-fm-${mod}.do + fm_shell -64 -file run-fm-${mod}.do 2>&1 | tee run-fm-${mod}.log + rsync -v -e "${REMOTE_YOSYS_SSH:-ssh}" run-fm-${mod}.log "$REMOTE_YOSYS_ROOT"/tests/or1200/ +done + +echo; echo +for x in run-fm-*.log; do + echo -e "${x%/*}\\t$( egrep '^Verification (SUCCEEDED|FAILED)' $x; )" +done | expand -t20 +echo; echo diff --git a/tests/or1200/run-fm.do b/tests/or1200/run-fm.do new file mode 100644 index 000000000..ed5c72282 --- /dev/null +++ b/tests/or1200/run-fm.do @@ -0,0 +1,53 @@ + +set hdlin_ignore_full_case false +set hdlin_warn_on_mismatch_message "FMR_ELAB-115 FMR_VLOG-079 FMR_VLOG-091" + +read_verilog -container r -libname WORK -01 rtl/or1200_alu.v +read_verilog -container r -libname WORK -01 rtl/or1200_amultp2_32x32.v +read_verilog -container r -libname WORK -01 rtl/or1200_cfgr.v +read_verilog -container r -libname WORK -01 rtl/or1200_cpu.v +read_verilog -container r -libname WORK -01 rtl/or1200_ctrl.v +read_verilog -container r -libname WORK -01 rtl/or1200_dc_fsm.v +read_verilog -container r -libname WORK -01 rtl/or1200_dc_ram.v +read_verilog -container r -libname WORK -01 rtl/or1200_dc_tag.v +read_verilog -container r -libname WORK -01 rtl/or1200_dc_top.v +read_verilog -container r -libname WORK -01 rtl/or1200_dmmu_tlb.v +read_verilog -container r -libname WORK -01 rtl/or1200_dmmu_top.v +read_verilog -container r -libname WORK -01 rtl/or1200_dpram.v +read_verilog -container r -libname WORK -01 rtl/or1200_du.v +read_verilog -container r -libname WORK -01 rtl/or1200_except.v +read_verilog -container r -libname WORK -01 rtl/or1200_fpu.v +read_verilog -container r -libname WORK -01 rtl/or1200_freeze.v +read_verilog -container r -libname WORK -01 rtl/or1200_genpc.v +read_verilog -container r -libname WORK -01 rtl/or1200_ic_fsm.v +read_verilog -container r -libname WORK -01 rtl/or1200_ic_ram.v +read_verilog -container r -libname WORK -01 rtl/or1200_ic_tag.v +read_verilog -container r -libname WORK -01 rtl/or1200_ic_top.v +read_verilog -container r -libname WORK -01 rtl/or1200_if.v +read_verilog -container r -libname WORK -01 rtl/or1200_immu_tlb.v +read_verilog -container r -libname WORK -01 rtl/or1200_immu_top.v +read_verilog -container r -libname WORK -01 rtl/or1200_lsu.v +read_verilog -container r -libname WORK -01 rtl/or1200_mem2reg.v +read_verilog -container r -libname WORK -01 rtl/or1200_mult_mac.v +read_verilog -container r -libname WORK -01 rtl/or1200_operandmuxes.v +read_verilog -container r -libname WORK -01 rtl/or1200_pic.v +read_verilog -container r -libname WORK -01 rtl/or1200_pm.v +read_verilog -container r -libname WORK -01 rtl/or1200_qmem_top.v +read_verilog -container r -libname WORK -01 rtl/or1200_reg2mem.v +read_verilog -container r -libname WORK -01 rtl/or1200_rf.v +read_verilog -container r -libname WORK -01 rtl/or1200_sb.v +read_verilog -container r -libname WORK -01 rtl/or1200_spram_32_bw.v +read_verilog -container r -libname WORK -01 rtl/or1200_spram.v +read_verilog -container r -libname WORK -01 rtl/or1200_sprs.v +read_verilog -container r -libname WORK -01 rtl/or1200_top.v +read_verilog -container r -libname WORK -01 rtl/or1200_tt.v +read_verilog -container r -libname WORK -01 rtl/or1200_wb_biu.v +read_verilog -container r -libname WORK -01 rtl/or1200_wbmux.v +set_top r:/WORK/or1200_top + +read_verilog -container i -libname WORK -01 synth.v +read_verilog -container i -technology_library -libname TECH_WORK -01 ../../techlibs/stdcells_sim.v +set_top i:/WORK/or1200_top + +if ![verify] start_gui exit + diff --git a/tests/or1200/run-fm.sh b/tests/or1200/run-fm.sh new file mode 100644 index 000000000..3023809c9 --- /dev/null +++ b/tests/or1200/run-fm.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if [ -n "$REMOTE_YOSYS_ROOT" ]; then + rsync --exclude=".svn" --exclude="*.log" -rv -e "${REMOTE_YOSYS_SSH:-ssh} -C" "$REMOTE_YOSYS_ROOT"/tests/or1200/. . +fi +fm_shell -64 -file run-fm.do 2>&1 | tee run-fm.log diff --git a/tests/or1200/run-synth.sh b/tests/or1200/run-synth.sh new file mode 100644 index 000000000..9f7e43fd6 --- /dev/null +++ b/tests/or1200/run-synth.sh @@ -0,0 +1,2 @@ +#!/bin/bash +time ../../yosys -b "verilog -noexpr" -o synth.v -tl synth.log -s run-synth.ys rtl/or1200_*.v 2>&1 | egrep '^\[[0-9.]+\] (ERROR|--|[0-9]+\.)' diff --git a/tests/or1200/run-synth.ys b/tests/or1200/run-synth.ys new file mode 100644 index 000000000..1f0d8a82b --- /dev/null +++ b/tests/or1200/run-synth.ys @@ -0,0 +1,11 @@ +hierarchy -check -top or1200_top +proc +opt +memory +opt +# fsm -norecode +# opt +techmap +opt +abc +opt diff --git a/tests/or1200/run-vg.sh b/tests/or1200/run-vg.sh new file mode 100644 index 000000000..54147cf5f --- /dev/null +++ b/tests/or1200/run-vg.sh @@ -0,0 +1,4 @@ +#!/bin/bash +time valgrind --leak-check=full --show-reachable=yes --log-file=valgrind.log \ + ../../yosys -o synth.v -tl synth.log -p "hierarchy -check -top or1200_top" \ + -p opt_const -p proc -p memory -p opt -p techmap -p opt -p abc -p opt rtl/or1200_*.v diff --git a/tests/simple/aes_kexp128.v b/tests/simple/aes_kexp128.v new file mode 100644 index 000000000..3ee034789 --- /dev/null +++ b/tests/simple/aes_kexp128.v @@ -0,0 +1,24 @@ + +// test taken from aes_core from iwls2005 + +module aes_key_expand_128(clk, kld, key, wo_0, wo_1, wo_2, wo_3); + +input clk, kld; +input [15:0] key; +output [3:0] wo_0, wo_1, wo_2, wo_3; +reg [3:0] w[3:0]; + +assign wo_0 = w[0]; +assign wo_1 = w[1]; +assign wo_2 = w[2]; +assign wo_3 = w[3]; + +always @(posedge clk) begin + w[0] <= kld ? key[15:12] : w[0]; + w[1] <= kld ? key[11: 8] : w[0]^w[1]; + w[2] <= kld ? key[ 7: 4] : w[0]^w[1]^w[2]; + w[3] <= kld ? key[ 3: 0] : w[0]^w[1]^w[2]^w[3]; +end + +endmodule + diff --git a/tests/simple/dff_different_styles.v b/tests/simple/dff_different_styles.v new file mode 100644 index 000000000..23d89b5dc --- /dev/null +++ b/tests/simple/dff_different_styles.v @@ -0,0 +1,52 @@ + +module dff(clk, d, q); +input clk, d; +output reg q; +always @(posedge clk) + q <= d; +endmodule + +module dffa(clk, arst, d, q); +input clk, arst, d; +output reg q; +always @(posedge clk or posedge arst) begin + if (arst) + q <= 1; + else + q <= d; +end +endmodule + +module dffa1(clk, arst, d, q); +input clk, arst, d; +output reg q; +always @(posedge clk or negedge arst) begin + if (~arst) + q <= 0; + else + q <= d; +end +endmodule + +module dffa2(clk, arst, d, q); +input clk, arst, d; +output reg q; +always @(posedge clk or negedge arst) begin + if (!arst) + q <= 0; + else + q <= d; +end +endmodule + +module dffa3(clk, arst, d, q); +input clk, arst, d; +output reg q; +always @(posedge clk or negedge arst) begin + if (~(!arst)) + q <= d; + else + q <= 1; +end +endmodule + diff --git a/tests/simple/fiedler-cooley.v b/tests/simple/fiedler-cooley.v new file mode 100644 index 000000000..96861973a --- /dev/null +++ b/tests/simple/fiedler-cooley.v @@ -0,0 +1,33 @@ +// borrowed with some modifications from +// http://www.ee.ed.ac.uk/~gerard/Teach/Verilog/manual/Example/lrgeEx2/cooley.html +module up3down5(clock, data_in, up, down, carry_out, borrow_out, count_out, parity_out); + +input [8:0] data_in; +input clock, up, down; + +output reg [8:0] count_out; +output reg carry_out, borrow_out, parity_out; + +reg [9:0] cnt_up, cnt_dn; +reg [8:0] count_nxt; + +always @(posedge clock) +begin + cnt_dn = count_out - 3'b 101; + cnt_up = count_out + 2'b 11; + + case ({up,down}) + 2'b 00 : count_nxt = data_in; + 2'b 01 : count_nxt = cnt_dn; + 2'b 10 : count_nxt = cnt_up; + 2'b 11 : count_nxt = count_out; + default : count_nxt = 9'bX; + endcase + + parity_out <= ^count_nxt; + carry_out <= up & cnt_up[9]; + borrow_out <= down & cnt_dn[9]; + count_out <= count_nxt; +end + +endmodule diff --git a/tests/simple/fsm.v b/tests/simple/fsm.v new file mode 100644 index 000000000..79ca041dd --- /dev/null +++ b/tests/simple/fsm.v @@ -0,0 +1,69 @@ + +// `define ASYNC_RESET + +module test(clk, reset, button_a, button_b, red_a, green_a, red_b, green_b); + +input clk, reset, button_a, button_b; +output reg red_a, green_a, red_b, green_b; + +(* gentb_constant = 0 *) +wire reset; + +integer state; +reg [3:0] cnt; + +`ifdef ASYNC_RESET +always @(posedge clk, posedge reset) +`else +always @(posedge clk) +`endif +begin + cnt <= 0; + red_a <= 1; + red_b <= 1; + green_a <= 0; + green_b <= 0; + + if (reset) + state <= 100; + else + case (state) + 100: begin + if (button_a && !button_b) + state <= 200; + if (!button_a && button_b) + state <= 300; + end + 200: begin + red_a <= 0; + green_a <= 1; + cnt <= cnt + 1; + if (cnt == 5) + state <= 210; + end + 210: begin + red_a <= 0; + green_a <= cnt[0]; + cnt <= cnt + 1; + if (cnt == 10) + state <= 100; + end + 300: begin + red_b <= 0; + green_b <= 1; + cnt <= cnt + 1; + if (cnt == 5) + state <= 310; + end + 310: begin + red_b <= 0; + green_b <= cnt[0]; + cnt <= cnt + 1; + if (cnt == 10) + state <= 100; + end + endcase +end + +endmodule + diff --git a/tests/simple/generate.v b/tests/simple/generate.v new file mode 100644 index 000000000..d458c076d --- /dev/null +++ b/tests/simple/generate.v @@ -0,0 +1,67 @@ + +module test1(clk, a, b, y); + +input clk; +input [7:0] a, b; +output reg [7:0] y; + +genvar i, j; +wire [15:0] tmp1; + +generate + + for (i = 0; i < 8; i = i + 1) begin:gen1 + wire and_wire, or_wire; + assign and_wire = a[i] & b[i]; + assign or_wire = a[i] | b[i]; + if (i % 2 == 0) begin:gen2true + assign tmp1[i] = and_wire; + assign tmp1[i+8] = or_wire; + end else begin:gen2false + assign tmp1[i] = or_wire; + assign tmp1[i+8] = and_wire; + end + end + + for (i = 0; i < 8; i = i + 1) begin:gen3 + wire [4:0] tmp2; + for (j = 0; j <= 4; j = j + 1) begin:gen4 + wire tmpbuf; + assign tmpbuf = tmp1[i+2*j]; + assign tmp2[j] = tmpbuf; + end + always @(posedge clk) + y[i] <= ^tmp2; + end + +endgenerate + +endmodule + +// ------------------------------------------ + +module test2(clk, a, b, y); + +input clk; +input [7:0] a, b; +output reg [8:0] y; + +integer i; +reg [8:0] carry; + +always @(posedge clk) begin + carry[0] = 0; + for (i = 0; i < 8; i = i + 1) begin + casez ({a[i], b[i], carry[i]}) + 3'b?11, 3'b1?1, 3'b11?: + carry[i+1] = 1; + default: + carry[i+1] = 0; + endcase + y[i] = a[i] ^ b[i] ^ carry[i]; + end + y[8] = carry[8]; +end + +endmodule + diff --git a/tests/simple/i2c_master_tests.v b/tests/simple/i2c_master_tests.v new file mode 100644 index 000000000..f8f564085 --- /dev/null +++ b/tests/simple/i2c_master_tests.v @@ -0,0 +1,62 @@ +// one of my early test cases was the OpenCores I2C master +// This is a collection of stripped down code snippets from +// this core that triggered bugs in early versions of yosys. + +// from i2c_master_bit_ctrl +module test01(clk, rst, nReset, al); + + input clk, rst, nReset; + output reg al; + + reg cmd_stop; + always @(posedge clk or negedge nReset) + if (~nReset) + cmd_stop <= #1 1'b0; + else if (rst) + cmd_stop <= #1 1'b0; + + always @(posedge clk or negedge nReset) + if (~nReset) + al <= #1 1'b0; + else if (rst) + al <= #1 1'b0; + else + al <= #1 ~cmd_stop; + +endmodule + +// from i2c_master_bit_ctrl +module test02(clk, slave_wait, clk_cnt, cmd, cmd_stop, cnt); + + input clk, slave_wait, clk_cnt; + input cmd; + + output reg cmd_stop; + + reg clk_en; + output reg [15:0] cnt; + + always @(posedge clk) + if (~|cnt) + if (~slave_wait) + begin + cnt <= #1 clk_cnt; + clk_en <= #1 1'b1; + end + else + begin + cnt <= #1 cnt; + clk_en <= #1 1'b0; + end + else + begin + cnt <= #1 cnt - 16'h1; + clk_en <= #1 1'b0; + end + + always @(posedge clk) + if (clk_en) + cmd_stop <= #1 cmd; + +endmodule + diff --git a/tests/simple/loops.v b/tests/simple/loops.v new file mode 100644 index 000000000..77cdcd8e2 --- /dev/null +++ b/tests/simple/loops.v @@ -0,0 +1,79 @@ + +// a simple test case extracted from systemcaes (as included in iwls2005) +// this design has latches (or logic loops) for the two temp variables. +// this latches (or logic loops) must be removed in the final synthesis results + +module aes( + // inputs + input [3:0] addroundkey_data_i, + input [3:0] addroundkey_data_reg, + input [3:0] addroundkey_round, + input [3:0] key_i, + input [3:0] keysched_new_key_o, + input [3:0] round, + input addroundkey_start_i, + input keysched_ready_o, + + // outputs + output reg [3:0] keysched_last_key_i, + output reg [3:0] keysched_round_i, + output reg [3:0] next_addroundkey_data_reg, + output reg [3:0] next_addroundkey_round, + output reg [3:0] round_data_var, + output reg keysched_start_i, + output reg next_addroundkey_ready_o +); + +// temp variables +reg [3:0] data_var; +reg [3:0] round_key_var; + +always @* +begin + keysched_start_i = 0; + keysched_round_i = addroundkey_round; + round_data_var = addroundkey_data_reg; + next_addroundkey_data_reg = addroundkey_data_reg; + next_addroundkey_ready_o = 0; + next_addroundkey_round = addroundkey_round; + + if (addroundkey_round == 1 || addroundkey_round == 0) + keysched_last_key_i = key_i; + else + keysched_last_key_i = keysched_new_key_o; + + if (round == 0 && addroundkey_start_i) + begin + data_var = addroundkey_data_i; + round_key_var = key_i; + round_data_var = round_key_var ^ data_var; + next_addroundkey_data_reg = round_data_var; + next_addroundkey_ready_o = 1; + end + else if (addroundkey_start_i && round != 0) + begin + keysched_last_key_i = key_i; + keysched_start_i = 1; + keysched_round_i = 1; + next_addroundkey_round = 1; + end + else if (addroundkey_round != round && keysched_ready_o) + begin + next_addroundkey_round = addroundkey_round + 1; + keysched_last_key_i = keysched_new_key_o; + keysched_start_i = 1; + keysched_round_i = addroundkey_round + 1; + end + else if (addroundkey_round == round && keysched_ready_o) + begin + data_var = addroundkey_data_i; + round_key_var = keysched_new_key_o; + round_data_var = round_key_var ^ data_var; + next_addroundkey_data_reg = round_data_var; + next_addroundkey_ready_o = 1; + next_addroundkey_round = 0; + end +end + +endmodule + diff --git a/tests/simple/mem2reg.v b/tests/simple/mem2reg.v new file mode 100644 index 000000000..7be32b0b3 --- /dev/null +++ b/tests/simple/mem2reg.v @@ -0,0 +1,17 @@ +module test1(in_addr, in_data, out_addr, out_data); + +input [1:0] in_addr, out_addr; +input [3:0] in_data; +output reg [3:0] out_data; + +reg [3:0] array [2:0]; + +always @* begin + array[0] = 0; + array[1] = 23; + array[2] = 42; + array[in_addr] = in_data; + out_data = array[out_addr]; +end + +endmodule diff --git a/tests/simple/memory.v b/tests/simple/memory.v new file mode 100644 index 000000000..c25bcd928 --- /dev/null +++ b/tests/simple/memory.v @@ -0,0 +1,19 @@ + +module test01(clk, wr_en, wr_addr, wr_value, rd_addr, rd_value); + +input clk, wr_en; +input [3:0] wr_addr, rd_addr; +input [7:0] wr_value; +output reg [7:0] rd_value; + +reg [7:0] data [15:0]; + +always @(posedge clk) + if (wr_en) + data[wr_addr] <= wr_value; + +always @(posedge clk) + rd_value <= data[rd_addr]; + +endmodule + diff --git a/tests/simple/muxtree.v b/tests/simple/muxtree.v new file mode 100644 index 000000000..6996206c0 --- /dev/null +++ b/tests/simple/muxtree.v @@ -0,0 +1,50 @@ + +// test case generated from IWLS 2005 usb_phy core +// (triggered a bug in opt_muxtree pass) + +module usb_tx_phy(clk, rst, DataOut_i, TxValid_i, hold_reg); + +input clk; +input rst; +input DataOut_i; +input TxValid_i; +output reg hold_reg; + +reg state, next_state; +reg ld_sop_d; +reg ld_data_d; + +always @(posedge clk) + if(ld_sop_d) + hold_reg <= 0; + else + hold_reg <= DataOut_i; + +always @(posedge clk) + if(!rst) state <= 0; + else state <= next_state; + +always @(state or TxValid_i) + begin + next_state = state; + + ld_sop_d = 1'b0; + ld_data_d = 1'b0; + + case(state) // synopsys full_case parallel_case + 0: + if(TxValid_i) + begin + ld_sop_d = 1'b1; + next_state = 1; + end + 1: + if(TxValid_i) + begin + ld_data_d = 1'b1; + next_state = 0; + end + endcase + end + +endmodule diff --git a/tests/simple/omsp_dbg_uart.v b/tests/simple/omsp_dbg_uart.v new file mode 100644 index 000000000..dc8860dee --- /dev/null +++ b/tests/simple/omsp_dbg_uart.v @@ -0,0 +1,34 @@ + +module omsp_dbg_uart (dbg_clk, dbg_rst, mem_burst, cmd_valid); + +input dbg_clk; +input dbg_rst; +input mem_burst; +output cmd_valid; + +reg [2:0] uart_state; +reg [2:0] uart_state_nxt; + +wire xfer_done; + +parameter RX_SYNC = 3'h0; +parameter RX_CMD = 3'h1; +parameter RX_DATA = 3'h2; + +always @(uart_state or mem_burst) + case (uart_state) + RX_SYNC : uart_state_nxt = RX_CMD; + RX_CMD : uart_state_nxt = mem_burst ? RX_DATA : RX_SYNC; + RX_DATA : uart_state_nxt = RX_SYNC; + default : uart_state_nxt = RX_CMD; + endcase + +always @(posedge dbg_clk or posedge dbg_rst) + if (dbg_rst) uart_state <= RX_SYNC; + else if (xfer_done | mem_burst) uart_state <= uart_state_nxt; + +assign cmd_valid = (uart_state==RX_CMD) & xfer_done; +assign xfer_done = uart_state!=RX_SYNC; + +endmodule + diff --git a/tests/simple/operators.v b/tests/simple/operators.v new file mode 100644 index 000000000..b9bbc13c8 --- /dev/null +++ b/tests/simple/operators.v @@ -0,0 +1,97 @@ + +module test(clk, mode, u1, s1, u2, s2, y); + +input clk; +input [5:0] mode; + +input [3:0] u1, u2; +input signed [3:0] s1, s2; + +output reg [7:0] y; + +always @(posedge clk) begin + y <= 8'h42; + case (mode) + 0: y <= u1 << u2; + 1: y <= u1 << s2; + 2: y <= s1 << u2; + 3: y <= s1 << s2; + + 4: y <= u1 >> u2; + 5: y <= u1 >> s2; + 6: y <= s1 >> u2; + 7: y <= s1 >> s2; + + 8: y <= u1 <<< u2; + 9: y <= u1 <<< s2; + 10: y <= s1 <<< u2; + 11: y <= s1 <<< s2; + + 12: y <= u1 >>> u2; + 13: y <= u1 >>> s2; + 14: y <= s1 >>> u2; + 15: y <= s1 >>> s2; + + 16: y <= u1 < u2; + 17: y <= u1 < s2; + 18: y <= s1 < u2; + 19: y <= s1 < s2; + + 20: y <= u1 <= u2; + 21: y <= u1 <= s2; + 22: y <= s1 <= u2; + 23: y <= s1 <= s2; + + 24: y <= u1 == u2; + 25: y <= u1 == s2; + 26: y <= s1 == u2; + 27: y <= s1 == s2; + + 28: y <= u1 != u2; + 29: y <= u1 != s2; + 30: y <= s1 != u2; + 31: y <= s1 != s2; + + 32: y <= u1 >= u2; + 33: y <= u1 >= s2; + 34: y <= s1 >= u2; + 35: y <= s1 >= s2; + + 36: y <= u1 > u2; + 37: y <= u1 > s2; + 38: y <= s1 > u2; + 39: y <= s1 > s2; + + 40: y <= u1 + u2; + 41: y <= u1 + s2; + 42: y <= s1 + u2; + 43: y <= s1 + s2; + + 44: y <= u1 - u2; + 45: y <= u1 - s2; + 46: y <= s1 - u2; + 47: y <= s1 - s2; + + 48: y <= +u1; + 49: y <= -u1; + 50: y <= +s1; + 51: y <= -s1; + + 52: y <= { &u1, ~&u1, |u1, ~|u1, ^u1, ~^u1, ^~u1 }; + 53: y <= { &s1, ~&s1, |s1, ~|s1, ^s1, ~^s1, ^~s1 }; + 54: y <= { &u1[1:0], ~&u1[1:0], |u1[1:0], ~|u1[1:0], ^u1[1:0], ~^u1[1:0], ^~u1[1:0] }; + 55: y <= { &s1[1:0], ~&s1[1:0], |s1[1:0], ~|s1[1:0], ^s1[1:0], ~^s1[1:0], ^~s1[1:0] }; + + 56: y <= { u1[1:0] && u2[1:0], u1[1:0] && u2[1:0], !u1[1:0] }; + 57: y <= {4{u1[1:0]}}; + 58: y <= {u1, u2} ^ {s1, s2}; + 59: y <= {u1, u2} & {s1, s2}; + + 60: y <= u1[0] ? u1 : u2; + 61: y <= u1[0] ? u1 : s2; + 62: y <= u1[0] ? s1 : u2; + 63: y <= u1[0] ? s1 : s2; + endcase +end + +endmodule diff --git a/tests/simple/paramods.v b/tests/simple/paramods.v new file mode 100644 index 000000000..94fd2dfc2 --- /dev/null +++ b/tests/simple/paramods.v @@ -0,0 +1,37 @@ + +module test1(a, b, x, y); + +input [7:0] a, b; +output [7:0] x, y; + +inc #(.step(3)) inc_a (.in(a), .out(x)); +inc #(.width(4), .step(7)) inc_b (b, y); + +endmodule + +// ----------------------------------- + +module test2(a, b, x, y); + +input [7:0] a, b; +output [7:0] x, y; + +inc #(5) inc_a (.in(a), .out(x)); +inc #(4, 7) inc_b (b, y); + +endmodule + +// ----------------------------------- + +module inc(in, out); + +parameter width = 8; +parameter step = 1; + +input [width-1:0] in; +output [width-1:0] out; + +assign out = in + step; + +endmodule + diff --git a/tests/simple/process.v b/tests/simple/process.v new file mode 100644 index 000000000..532586649 --- /dev/null +++ b/tests/simple/process.v @@ -0,0 +1,65 @@ + +module uut(clk, arst, a, b, c, d, e, f, out1); + +input clk, arst, a, b, c, d, e, f; +output reg [3:0] out1; + +always @(posedge clk, posedge arst) begin + if (arst) + out1 = 0; + else begin + if (a) begin + case ({b, c}) + 2'b00: + out1 = out1 + 9; + 2'b01, 2'b10: + out1 = out1 + 13; + endcase + if (d) begin + out1 = out1 + 2; + out1 = out1 + 1; + end + case ({e, f}) + 2'b11: + out1 = out1 + 8; + 2'b00: + ; + default: + out1 = out1 + 10; + endcase + out1 = out1 ^ 7; + end + out1 = out1 + 14; + end +end + +endmodule + +// ------------------------------------------------------------- + +// extracted from ../asicworld/code_hdl_models_uart.v +// (triggered a bug in the proc_mux pass) +module uart (reset, txclk, ld_tx_data, tx_empty, tx_cnt); + +input reset; +input txclk; +input ld_tx_data; + +output reg tx_empty; +output reg [3:0] tx_cnt; + +always @ (posedge txclk) +if (reset) begin + tx_empty <= 1; + tx_cnt <= 0; +end else begin + if (ld_tx_data) begin + tx_empty <= 0; + end + if (!tx_empty) begin + tx_cnt <= tx_cnt + 1; + end +end + +endmodule + diff --git a/tests/simple/run-test.sh b/tests/simple/run-test.sh new file mode 100755 index 000000000..bf27d15f8 --- /dev/null +++ b/tests/simple/run-test.sh @@ -0,0 +1,3 @@ +#!/bin/bash +make -C ../.. || exit 1 +exec bash ../tools/autotest.sh *.v diff --git a/tests/simple/subbytes.v b/tests/simple/subbytes.v new file mode 100644 index 000000000..04269a99f --- /dev/null +++ b/tests/simple/subbytes.v @@ -0,0 +1,82 @@ + +// test taken from systemcaes from iwls2005 + +module subbytes_00(clk, reset, start_i, decrypt_i, data_i, ready_o, data_o, sbox_data_o, sbox_data_i, sbox_decrypt_o); + +input clk; +input reset; +input start_i; +input decrypt_i; +input [31:0] data_i; +output ready_o; +output [31:0] data_o; +output [7:0] sbox_data_o; +input [7:0] sbox_data_i; +output sbox_decrypt_o; + +reg ready_o; +reg [31:0] data_o; +reg [7:0] sbox_data_o; +reg sbox_decrypt_o; + +reg [1:0] state; +reg [1:0] next_state; +reg [31:0] data_reg; +reg [31:0] next_data_reg; +reg next_ready_o; + +always @(posedge clk or negedge reset) +begin + if (!reset) begin + data_reg = 0; + state = 0; + ready_o = 0; + end else begin + data_reg = next_data_reg; + state = next_state; + ready_o = next_ready_o; + end +end + +reg [31:0] data_i_var, data_reg_128; +reg [7:0] data_array [3:0]; +reg [7:0] data_reg_var [3:0]; + +always @(decrypt_i or start_i or state or data_i or sbox_data_i or data_reg) +begin + data_i_var = data_i; + + data_array[0] = data_i_var[ 31: 24]; + data_array[1] = data_i_var[ 23: 16]; + data_array[2] = data_i_var[ 15: 8]; + data_array[3] = data_i_var[ 7: 0]; + + data_reg_var[0] = data_reg[ 31: 24]; + data_reg_var[1] = data_reg[ 23: 16]; + data_reg_var[2] = data_reg[ 15: 8]; + data_reg_var[3] = data_reg[ 7: 0]; + + sbox_decrypt_o = decrypt_i; + sbox_data_o = data_array[state]; + next_state = state; + next_data_reg = data_reg; + + next_ready_o = 0; + data_o = data_reg; + + if (state) begin + if (start_i) begin + next_state = 1; + end + end else begin + data_reg_var[state] = sbox_data_i; + data_reg_128[ 31: 24] = data_reg_var[0]; + data_reg_128[ 23: 16] = data_reg_var[1]; + data_reg_128[ 15: 8] = data_reg_var[2]; + data_reg_128[ 7: 0] = data_reg_var[3]; + next_data_reg = data_reg_128; + next_state = state + 1; + end +end + +endmodule diff --git a/tests/simple/task_func.v b/tests/simple/task_func.v new file mode 100644 index 000000000..3a09cbc35 --- /dev/null +++ b/tests/simple/task_func.v @@ -0,0 +1,35 @@ + +module test01(clk, a, b, c, x, y, z, w); + +input clk; +input [7:0] a, b, c; +output reg [7:0] x, y, z, w; + +function [7:0] sum_shift; +input [3:0] s1, s2, s3; +sum_shift = s1 + (s2 << 2) + (s3 << 4); +endfunction + +task reset_w; +w = 0; +endtask + +task add_to; +output [7:0] out; +input [7:0] in; +out = out + in; +endtask + +always @(posedge clk) begin + x = sum_shift(a, b, c); + y = sum_shift(a[7:4], b[5:2], c[3:0]); + z = sum_shift(a[0], b[5:4], c >> 5) ^ sum_shift(1, 2, 3); + + reset_w; + add_to(w, x); + add_to(w, y); + add_to(w, z); +end + +endmodule + diff --git a/tests/simple/usb_phy_tetsts.v b/tests/simple/usb_phy_tetsts.v new file mode 100644 index 000000000..2375183d8 --- /dev/null +++ b/tests/simple/usb_phy_tetsts.v @@ -0,0 +1,36 @@ + +// from usb_rx_phy +module test01(clk, rst, rx_en, fs_ce); + +input clk, rst; +input rx_en; +output reg fs_ce; +reg [1:0] dpll_next_state; +reg [1:0] dpll_state; + +always @(posedge clk) + dpll_state <= rst ? 0 : dpll_next_state; + +always @* + begin + fs_ce = 1'b0; + case(dpll_state) + 2'h0: + if(rx_en) dpll_next_state = 2'h0; + else dpll_next_state = 2'h1; + 2'h1:begin + fs_ce = 1'b1; + if(rx_en) dpll_next_state = 2'h3; + else dpll_next_state = 2'h2; + end + 2'h2: + if(rx_en) dpll_next_state = 2'h0; + else dpll_next_state = 2'h3; + 2'h3: + if(rx_en) dpll_next_state = 2'h0; + else dpll_next_state = 2'h0; + endcase + end + +endmodule + diff --git a/tests/simple/values.v b/tests/simple/values.v new file mode 100644 index 000000000..9fae4da9d --- /dev/null +++ b/tests/simple/values.v @@ -0,0 +1,44 @@ + +module test_signed(a, b, c, d, y); + +input [3:0] a, b, c; +input signed [3:0] d; +output reg [7:0] y; + +always @* begin + if (a && b) + y = c; + else + y = d; +end + +endmodule + +module test_const(a, y); + +input [3:0] a; +output reg [28:0] y; + +always @* + case (a) + 4'b0000: y = 0; + 4'b0001: y = 11; + 4'b0010: y = 222; + 4'b0011: y = 3456; + 4'b0100: y = 'b10010010; + 4'b0101: y = 'h123abc; + 4'b0110: y = 'o1234567; + 4'b0111: y = 'd3456789; + 4'b1000: y = 16'b10010010; + 4'b1001: y = 16'h123abc; + 4'b1010: y = 16'o1234567; + 4'b1011: y = 16'd3456789; + 4'b1100: y = "foobar"; + 4'b1101: y = "foobarfoobarfoobar"; + 4'b1110: y = 16'h1; + 4'b1111: y = a; + default: y = 'bx; + endcase + +endmodule + diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh new file mode 100755 index 000000000..6b22f9023 --- /dev/null +++ b/tests/tools/autotest.sh @@ -0,0 +1,164 @@ +#!/bin/bash + +libs="" +genvcd=false +use_isim=false +verbose=false +keeprunning=false +backend_opts="-noattr -noexpr" +kompare_xst=false +scriptfiles="" +toolsdir="$(cd $(dirname $0); pwd)" + +if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then + ( set -ex; gcc -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 +fi + +while getopts il:wkvrxs: opt; do + case "$opt" in + i) + use_isim=true ;; + l) + libs="$libs $(cd $(dirname $OPTARG); pwd)/$(basename $OPTARG)";; + w) + genvcd=true ;; + k) + keeprunning=true ;; + v) + verbose=true ;; + r) + backend_opts="$backend_opts norename" ;; + x) + kompare_xst=true ;; + s) + [[ "$OPTARG" == /* ]] || OPTARG="$PWD/$OPTARG" + scriptfiles="$scriptfiles $OPTARG" ;; + *) + echo "Usage: $0 [-i] [-w] [-k] [-v] [-r] [-x] [-l libs] [-s script] verilog-files\n" >&2 + exit 1 + esac +done + +create_ref() { + if $kompare_xst; then + echo "verilog work $1" > $2.prj + cat <<- EOT > $2.xst + run + -ifn $2.prj -ifmt mixed -ofn $2 -ofmt NGC -p xc6slx4-3-tqg144 + -top $( grep ^module $1 | sed -r 's,[^0-9A-Za-z_]+, ,g' | awk '{ print $2; exit; }'; ) + -opt_mode Speed -opt_level 1 -iobuf NO + EOT + ( + set +x + prefix="$2" + xilver=$( ls -v /opt/Xilinx/ | tail -n1; ) + case "$( uname -m )" in + x86_64) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings64.sh ;; + *) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings32.sh ;; + esac + set -x + xst -ifn $prefix.xst + netgen -w -ofmt verilog $prefix.ngc $prefix + ) + else + cp "$1" "$2.v" + fi +} + +compile_and_run() { + exe="$1"; output="$2"; shift 2 + if $use_isim; then + ( + set +x + files=( "$@" ) + xilver=$( ls -v /opt/Xilinx/ | tail -n1; ) + case "$( uname -m )" in + x86_64) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings64.sh ;; + *) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings32.sh ;; + esac + set -x + vlogcomp "${files[@]}" + if $kompare_xst; then + fuse -o "$exe" -lib unisims_ver -top testbench -top glbl + else + fuse -o "$exe" -top testbench + fi + { echo "run all"; echo "exit"; } > run-all.tcl + PATH="$PATH:" "$exe" -tclbatch run-all.tcl > "$output" + ) + else + iverilog -s testbench -o "$exe" "$@" + vvp -n "$exe" > "$output" + fi +} + +shift $((OPTIND - 1)) + +for fn +do + bn=${fn%.v} + if [ "$bn" == "$fn" ]; then + echo "Invalid argument: $fn" >&2 + exit 1 + fi + [[ "$bn" == *_tb ]] && continue + echo -n "Test: $bn " + + rm -f ${bn}.{err,log} + mkdir -p ${bn}.out + rm -rf ${bn}.out/* + + body() { + cd ${bn}.out + cp ../$fn $fn + if [ ! -f ../${bn}_tb.v ]; then + "$toolsdir"/../../yosys -b autotest -o ${bn}_tb.v $fn + else + cp ../${bn}_tb.v ${bn}_tb.v + fi + if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi + create_ref $fn ${bn}_ref + compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs + if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi + + test_count=0 + test_passes() { + "$toolsdir"/../../yosys -b "verilog $backend_opts" "$@" -o ${bn}_syn${test_count}.v $fn $scriptfiles + compile_and_run ${bn}_tb_syn${test_count} ${bn}_out_syn${test_count} \ + ${bn}_tb.v ${bn}_syn${test_count}.v $libs \ + "$toolsdir"/../../techlibs/simlib.v \ + "$toolsdir"/../../techlibs/stdcells_sim.v + if $genvcd; then mv testbench.vcd ${bn}_syn${test_count}.vcd; fi + $toolsdir/cmp_tbdata ${bn}_out_ref ${bn}_out_syn${test_count} + test_count=$(( test_count + 1 )) + } + + if [ -n "$scriptfiles" ]; then + test_passes + else + test_passes -p hierarchy -p proc -p memory -p opt -p fsm -p opt + test_passes -p hierarchy -p proc -p memory -p opt -p fsm -p opt -p techmap -p opt + # test_passes -p hierarchy -p proc -p memory -p opt -p techmap -p opt -p abc -p opt + fi + touch ../${bn}.log + } + + if $verbose; then + echo ".." + echo "Output written to console." > ${bn}.err + ( set -ex; body; ) + else + ( set -ex; body; ) > ${bn}.err 2>&1 + fi + + if [ -f ${bn}.log ]; then + mv ${bn}.err ${bn}.log + echo "-> ok" + else echo "-> ERROR!"; $keeprunning || exit 1; fi +done + +exit 0 diff --git a/tests/tools/cmp_tbdata.c b/tests/tools/cmp_tbdata.c new file mode 100644 index 000000000..86485efd0 --- /dev/null +++ b/tests/tools/cmp_tbdata.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +int line = 0; +char buffer1[1024]; +char buffer2[1024]; + +void check(bool ok) +{ + if (ok) + return; + fprintf(stderr, "Error in testbench output compare (line=%d):\n-%s\n+%s\n", line, buffer1, buffer2); + exit(1); +} + +int main(int argc, char **argv) +{ + FILE *f1, *f2; + bool eof1, eof2; + int i; + + check(argc == 3); + + f1 = fopen(argv[1], "r"); + f2 = fopen(argv[2], "r"); + + check(f1 && f2); + + while (!feof(f1) && !feof(f2)) + { + line++; + buffer1[0] = 0; + buffer2[0] = 0; + + eof1 = fgets(buffer1, 1024, f1) == NULL; + eof2 = fgets(buffer2, 1024, f2) == NULL; + + if (*buffer1 && buffer1[strlen(buffer1)-1] == '\n') + buffer1[strlen(buffer1)-1] = 0; + + if (*buffer2 && buffer2[strlen(buffer2)-1] == '\n') + buffer2[strlen(buffer2)-1] = 0; + + check(eof1 == eof2); + + for (i = 0; buffer1[i] || buffer2[i]; i++) + { + check(buffer1[i] != 0 && buffer2[i] != 0); + + // first argument is the reference. An 'z' or 'x' + // here means we don't care about the result. + if (buffer1[i] == 'z' || buffer1[i] == 'x') + continue; + + check(buffer1[i] == buffer2[i]); + } + } + + check(feof(f1) && feof(f2)); + + fclose(f1); + fclose(f2); + return 0; +} + diff --git a/tests/tools/profiler.pl b/tests/tools/profiler.pl new file mode 100755 index 000000000..456f634bc --- /dev/null +++ b/tests/tools/profiler.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl +# parse 'yosys -t' logfile and find slow passes + +my $max_depth = 0; +my %last_line_by_depth; +my %last_time_by_depth; + +my @lines_text; +my @lines_depth; +my @lines_time; + +while (<>) +{ + chomp; + next unless /^\[([0-9.]+)\] (([0-9]+\.)+)/; + my ($this_time, $this_id, $this_header) = ($1, $2, $4); + + push @lines_text, $_; + push @lines_depth, 0; + push @lines_time, 0; + + my $depth = $this_id; + $depth =~ s/[^.]//g; + $depth = length $depth; + $max_depth = $depth if $depth > $max_depth; + + for (my $i = $depth; $i <= $max_depth; $i++) { + next unless exists $last_time_by_depth{$i}; + $lines_time[$last_line_by_depth{$i}] = $this_time-$last_time_by_depth{$i}; + delete $last_time_by_depth{$i}; + delete $last_header_by_depth{$i}; + } + + $last_time_by_depth{$depth} = $this_time; + $last_line_by_depth{$depth} = $#lines_text; + $lines_depth[$#lines_text] = $depth; +} + +for (my $depth = 1; $depth <= $max_depth; $depth++) { + printf "\nSlow passes on recursion depth %d:\n", $depth; + my @lines; + for (my $i = 0; $i <= $#lines_text; $i++) { + next if $lines_depth[$i] != $depth or $lines_time[$i] < 1.0; + push @lines, sprintf("%3d %08.2f %s\n", $lines_depth[$i], $lines_time[$i], $lines_text[$i]); + } + for my $line (sort {$b cmp $a} @lines) { + print $line; + } +} + +printf "\nFull journal of headers:\n"; +for (my $i = 0; $i <= $#lines_text; $i++) { + printf "%3d %08.2f %s\n", $lines_depth[$i], $lines_time[$i], $lines_text[$i]; +} + diff --git a/tests/tools/rtlview.sh b/tests/tools/rtlview.sh new file mode 100755 index 000000000..6a4adcae1 --- /dev/null +++ b/tests/tools/rtlview.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# using Xilinx ISE to display RTL schematics + +if [ ! -f "$1" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +prjdir="$(dirname $0)/rtlview.tmp" +mkdir -p "$prjdir" + +cp "$1" "$prjdir"/schematic.v +cp "$(dirname $0)"/../../techlibs/blackbox.v "$prjdir"/blackbox.v +cd "$prjdir" + +if fuser -s ise.out; then + echo "ISE already running. Re-create RTL schematic from GUI." + exit 1 +fi + +xilver=$( ls -v /opt/Xilinx/ | grep '^[0-9]' | tail -n1; ) + +cat > rtlview.xise << EOT + + +
+ + + + + + + + + + + + + + + + + + + + + + + +EOT + +set -- +case "$( uname -m )" in +x86_64) + . /opt/Xilinx/$xilver/ISE_DS/settings64.sh ;; +*) + . /opt/Xilinx/$xilver/ISE_DS/settings32.sh ;; +esac + +ise rtlview.xise > ise.out 2>&1 & +echo "ISE is now starting up. Create RTL schematic from GUI." + diff --git a/tests/tools/vcdcd.pl b/tests/tools/vcdcd.pl new file mode 100755 index 000000000..4875eeeb0 --- /dev/null +++ b/tests/tools/vcdcd.pl @@ -0,0 +1,201 @@ +#!/usr/bin/perl -w +# +# Note: You might need to install the Verilog::VCD package using CPAN.. + +use strict; +use Data::Dumper; +use Verilog::VCD qw(parse_vcd list_sigs); + +$| = 1; + +if ($#ARGV != 1) { + print STDERR "\n"; + print STDERR "VCDCD - Value Change Dump Change Dumper\n"; + print STDERR "\n"; + print STDERR "Usage: $0 gold.vcd gate.vcd\n"; + print STDERR "\n"; + print STDERR "Compare a known-good (gold) vcd file with a second (gate) vcd file.\n"; + print STDERR "This is not very efficient -- so use with care with large vcd files.\n"; + print STDERR "\n"; + exit 1; +} + +my $fn_gold = $ARGV[0]; +my $fn_gate = $ARGV[1]; + +print "Finding common signals..\n"; +my @gold_signals = list_sigs($fn_gold); +my @gate_signals = list_sigs($fn_gate); + +my %gold_signals_hash; +my %gate_signals_hash; + +for (@gold_signals) { + my $fullname = $_; + s/(\[([0-9]+|[0-9]+:[0-9]+)\])$//; + $gold_signals_hash{$_}->{$fullname} = 1 unless /(^|\.)_[0-9]+_/; +} + +for (@gate_signals) { + my $fullname = $_; + s/(\[([0-9]+|[0-9]+:[0-9]+)\])$//; + $gate_signals_hash{$_}->{$fullname} = 1 unless /(^|\.)_[0-9]+_/; +} + +my @signals; +for my $net (sort keys %gold_signals_hash) { + next unless exists $gate_signals_hash{$net}; + # next unless $net eq "tst_bench_top.i2c_top.byte_controller.bit_controller.cnt"; + my %orig_net_names; + print "common signal: $net"; + for my $fullname (keys $gold_signals_hash{$net}) { + $orig_net_names{$fullname} = 1; + } + for my $fullname (keys $gate_signals_hash{$net}) { + $orig_net_names{$fullname} = 1; + } + for my $_ (sort keys %orig_net_names) { + push @signals, $_; + print " $1" if /(\[([0-9]+|[0-9]+:[0-9]+)\])$/; + } + print "\n"; +} + +print "Loading gold vcd data..\n"; +my $vcd_gold = parse_vcd($fn_gold, {siglist => \@signals}); + +print "Loading gate vcd data..\n"; +my $vcd_gate = parse_vcd($fn_gate, {siglist => \@signals}); + +# print Dumper($vcd_gold); +# print Dumper($vcd_gate); + +my %times; +my $signal_maxlen = 8; +my $data_gold = { }; +my $data_gate = { }; + +sub checklen($$) +{ + my ($net, $val) = @_; + my $thislen = length $val; + $thislen += $1 if $net =~ /\[([0-9]+)\]$/; + $thislen += $1 if $net =~ /\[([0-9]+):[0-9]+\]$/; + $signal_maxlen = $thislen if $signal_maxlen < $thislen; +} + +print "Processing gold vcd data..\n"; +for my $key (keys %$vcd_gold) { + for my $net (@{$vcd_gold->{$key}->{'nets'}}) { + my $netname = $net->{'hier'} . "." . $net->{'name'}; + for my $tv (@{$vcd_gold->{$key}->{'tv'}}) { + my $time = int($tv->[0]); + my $value = $tv->[1]; + checklen($netname, $value); + $data_gold->{$time}->{$netname} = $value; + $times{$time} = 1; + } + } +} + +print "Processing gate vcd data..\n"; +for my $key (keys %$vcd_gate) { + for my $net (@{$vcd_gate->{$key}->{'nets'}}) { + my $netname = $net->{'hier'} . "." . $net->{'name'}; + for my $tv (@{$vcd_gate->{$key}->{'tv'}}) { + my $time = int($tv->[0]); + my $value = $tv->[1]; + checklen($netname, $value); + $data_gate->{$time}->{$netname} = $value; + $times{$time} = 1; + } + } +} + +my $diffcount = 0; +my %state_gold; +my %state_gate; +my %signal_sync; +my %touched_nets; + +sub set_state_bit($$$$) +{ + my ($state, $net, $bit, $value) = @_; + my @data; + @data = split //, $state->{$net} if exists $state->{$net}; + unshift @data, "-" while $#data < $bit; + $data[$#data - $bit] = $value; + $state->{$net} = join "", @data; + $signal_sync{$net} = 1 unless exists $signal_sync{$net}; + $touched_nets{$net} = 1; +} + +sub set_state($$$) +{ + my ($state, $net, $value) = @_; + + if ($net =~ /(.*)\[([0-9]+)\]$/) { + set_state_bit($state, $1, $2, $value); + return; + } + + if ($net =~ /(.*)\[([0-9]+):([0-9]+)\]$/) { + my ($n, $u, $d) = ($1, $2, $3); + my @bits = split //, $value; + my $extbit = $bits[0] eq "1" ? "0" : $bits[0]; + unshift @bits, $extbit while $#bits < $u - $d; + set_state_bit($state, $n, $u--, shift @bits) while $u >= $d; + return; + } + + $state->{$net} = $value; + $signal_sync{$net} = 1 unless exists $signal_sync{$net}; + $touched_nets{$net} = 1; +} + +sub cmp_signal($$) +{ + my ($a, $b) = @_; + return 1 if $a eq $b; + + my @a = split //, $a; + my @b = split //, $b; + + unshift @a, "-" while $#a < $#b; + unshift @b, "-" while $#b < $#a; + + for (my $i = 0; $i <= $#a; $i++) { + return 0 if $a[$i] ne "x" && $a[$i] ne $b[$i]; + } + + return 1; +} + +print "Comparing vcd data..\n"; +for my $time (sort { $a <=> $b } keys %times) +{ + %touched_nets = (); + for my $net (keys %{$data_gold->{$time}}) { + set_state(\%state_gold, $net, $data_gold->{$time}->{$net}); + } + for my $net (keys %{$data_gate->{$time}}) { + set_state(\%state_gate, $net, $data_gate->{$time}->{$net}); + } + for my $net (sort keys %touched_nets) { + my ($stgo, $stga) = ('-', '-'); + $stgo = $state_gold{$net} if exists $state_gold{$net}; + $stga = $state_gate{$net} if exists $state_gate{$net}; + if (cmp_signal($stgo, $stga)) { + next if $signal_sync{$net}; + printf "%-10s %-20d %-*s %-*s %s\n", "", $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net; + $signal_sync{$net} = 1; + } else { + printf "\n%-10s %-20s %-*s %-*s %s\n", "count", "time", $signal_maxlen, "gold", $signal_maxlen, "gate", "net" if $diffcount++ == 0; + printf "%-10d %-20d %-*s %-*s %s\n", $diffcount, $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net; + $signal_sync{$net} = 0; + } + } +} + +print "Found $diffcount differences.\n"; +exit ($diffcount > 0 ? 1 : 0); -- 2.30.2