ラベル ICS の投稿を表示しています。 すべての投稿を表示
ラベル ICS の投稿を表示しています。 すべての投稿を表示

2020年10月8日木曜日

UVMのreactive slaveを作ってみた話

UVMでslave動作するモデルを作成してみた話。実装したものはこちら。命名が微妙なのは諦めている。

Verilabの資料を参考に作成してみた。 資料とは異なり、sequencerはuvm_sequencerを使用し、その代わりにconfig_dbを使用してreact_req_fifoをsequenceから利用できるようにしている。参照させるためだけにクラス定義もあれかと思ったので。
加えてmonitor_icsからstorageのアップデートをせず、ics_reactive_seqに管理を一任している。passiveで使うならslaveでないものを使用すれば良いし、その状況下でstorageが必要になるとも思えないと思ったので。

 



プロトコル変換するのと同様に無限ループするシーケンスを用意し、react_req_fifoからデータを取り出して、応答データを作成してsequencerに実行させている。
get_sequencer()でsequencerに設定された設定値をsequenceで取得できるので使用している。
該当の実装は以下の通り。

class simple_ics_slave_env extends uvm_env;
    `uvm_component_utils(simple_ics_slave_env)
    `uvm_new_func
    simple_ics_env ics_env;
    simple_ics_slave_storage storage;
    uvm_tlm_analysis_fifo#(simple_ics_seq_item) react_req_fifo;
    //uvm_sequencer#(simple_ics_seq_item) sequencer;
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        ics_env = simple_ics_env::type_id::create("ics_env",this);
        react_req_fifo = new("react_req_fifo",this);
        storage = simple_ics_slave_storage::type_id::create("storage",this);
        storage.init();
        uvm_config_db #(uvm_tlm_analysis_fifo#(simple_ics_seq_item))::set(this, "*", "react_req_fifo", react_req_fifo);
        uvm_config_db #(simple_ics_slave_storage)::set(this, "*", "storage", storage);
    endfunction

    function void connect_phase(uvm_phase phase);
        ics_env.monitor.react_req_port.connect(react_req_fifo.analysis_export);
    endfunction

    task run_phase(uvm_phase phase);
        ics_reactive_seq reactive_seq;
        reactive_seq = ics_reactive_seq::type_id::create("reactive_seq",this);
        reactive_seq.start(ics_env.sequencer);
    endtask
endclass
class ics_reactive_seq extends uvm_sequence #(simple_ics_seq_item);
    `uvm_object_utils(ics_reactive_seq)
    simple_ics_seq_item received_item;
    simple_ics_seq_item responce_item;
    uvm_tlm_analysis_fifo#(simple_ics_seq_item) react_req_fifo;
    simple_ics_slave_storage storage;
    int baud_rate;
    int bit_period;
    int stop_bit_num;

    function new(string name="ics_reactive_seq");
        super.new(name);
    endfunction

    task body();
        logic[7:0]      rdata;

        if(!uvm_config_db #(uvm_tlm_analysis_fifo#(simple_ics_seq_item))::get(get_sequencer(), "", "react_req_fifo", react_req_fifo)) begin
            `uvm_fatal("CONFIG_DB_ERROR", "Could not find react_req_fifo")
        end
        if(!uvm_config_db #(simple_ics_slave_storage)::get(get_sequencer(),"", "storage", storage)) begin
            `uvm_fatal("CONFIG_DB_ERROR", "Could not find simple_ics_slave_storage")
        end
        //baud rate
        if(!uvm_config_db#(int)::get(get_sequencer(), "", "simple_uart_baud_rate", baud_rate)) begin
            uvm_report_info("CONFIG","baud rate set to default value(115200 bps)");
            baud_rate = 115200;//default value
        end
        case(baud_rate)
            115200: bit_period = 8681;//8680.6 ns for 115200 bps.
            default: `uvm_fatal("CONFIG_ERR","Undefined baud rate.")
        endcase
        //number of stop bit
        if(!uvm_config_db#(int)::get(get_sequencer(), "", "simple_uart_stop_bit_num", stop_bit_num)) begin
            uvm_report_info("CONFIG","number of stop bit set to default value(1bit)");
            stop_bit_num = 1;//default value
        end

        forever begin
            react_req_fifo.get(received_item);
            uvm_report_info("REACT","get item");
            #(bit_period*(0.2+stop_bit_num));
            received_item.print();
            responce_item = new();

            case(received_item.cmd)
                simple_ics_seq_item::POSITION_R: uvm_report_fatal("REACT", "Invalid POSITION_R command received.");
                simple_ics_seq_item::READ_R: uvm_report_fatal("REACT", "Invalid READ_R command received.");
                simple_ics_seq_item::WRITE_R: uvm_report_fatal("REACT", "Invalid WRITE_R command received.");
                simple_ics_seq_item::ID_R: uvm_report_fatal("REACT", "Invalid ID_R command received.");
                simple_ics_seq_item::POSITION:
                    begin
                        storage.write_reg(received_item.id, simple_ics_seq_item::TCH, received_item.data);
                        responce_item.cmd = simple_ics_seq_item::POSITION_R;
                        responce_item.id = received_item.id;
                        responce_item.data = storage.read_reg(received_item.id, simple_ics_seq_item::TCH);
                    end
                simple_ics_seq_item::READ:
                    begin
                        responce_item.cmd = simple_ics_seq_item::READ_R;
                        responce_item.id = received_item.id;
                        if(received_item.sub_cmd === simple_ics_seq_item::EEPROM) begin
                            logic[7:0] eeprom[0:63];
                            //responce_item.eeprom_data = new[64];
                            storage.read_mem(received_item.id, eeprom);
                            responce_item.eeprom_data = eeprom;
                        end else begin
                            responce_item.sub_cmd = received_item.sub_cmd;
                            responce_item.data = storage.read_reg(received_item.id, received_item.sub_cmd);
                        end
                    end
                simple_ics_seq_item::WRITE:
                    begin
                        responce_item.cmd = simple_ics_seq_item::WRITE_R;
                        responce_item.id = received_item.id;
                        if(received_item.sub_cmd === simple_ics_seq_item::EEPROM) begin
                            storage.write_mem(received_item.id, received_item.eeprom_data);
                            responce_item.sub_cmd = simple_ics_seq_item::EEPROM;
                            responce_item.data = 0;
                        end else begin
                            storage.write_reg(received_item.id, received_item.sub_cmd, received_item.data);
                            responce_item.sub_cmd = received_item.sub_cmd;
                            responce_item.data = storage.read_reg(received_item.id, received_item.sub_cmd);
                        end
                    end
                simple_ics_seq_item::ID:
                    begin
                        uvm_report_fatal("REACT", "ID command is not implemented yet.");
                    end
            endcase
            responce_item.print();
            `uvm_send(responce_item);
        end
    endtask

endclass

2020年10月6日火曜日

UVM1.2でプロトコル変換してみた

UVMでプロトコル変換を試してみた話。ICSを題材に実装したものはこちら。

Verification Aacademyの記事を参考に作成してみた。

通常のAgentの外側にプロトコルレイヤーを処理するシーケンス、シーケンサー、モニターを追加するだけ。 実装はUARTの通信インターフェースを使ってデータを交換するICSのモデルを作成した。


 

下位から上位へのプロトコル変換はmonitorから受け取ったitemを順次処理すれば良い。 ポイントは下位のsequencerでプロトコル変換用のsequenceを動作させること。
このシーケンスが上位のsequencerのitemを取得し、 それを現在動作しているsequencerの実行アイテムに分してdriverに送り込むことでプロトコルの変換が行われる。
該当の実装は以下の通り。

class ics_item_to_uart_item_seq extends uvm_sequence #(simple_uart_seq_item);
    `uvm_object_utils(ics_item_to_uart_item_seq)
    function new(string name="ics_item_to_uart_item");
        super.new(name);
    endfunction

    uvm_sequencer #(simple_ics_seq_item) up_sequencer;

    virtual task body();
        simple_ics_seq_item ics_item;
        simple_uart_seq_item uart_item;
        logic[4:0] id;
        bit enable_item_port = 0;
        forever begin
            up_sequencer.get_next_item(ics_item);
            uart_item = simple_uart_seq_item::type_id::create("test");
            id = ics_item.id;
            `uvm_create(uart_item)
            case(ics_item.cmd)
                simple_ics_seq_item::POSITION_R:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::POSITION_R,id};})
                        `uvm_do_with(uart_item, {char=={0,ics_item.data[13:7]};})
                        `uvm_do_with(uart_item, {char=={0,ics_item.data[ 6:0]};})
                    end
                simple_ics_seq_item::READ_R:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::READ_R,id};})
                        `uvm_do_with(uart_item, {char==ics_item.sub_cmd;})
                        if(ics_item.sub_cmd === simple_ics_seq_item::EEPROM) begin
                            for(int i = 0; i < 64; ++i) begin
                                `uvm_do_with(uart_item, {char==ics_item.eeprom_data[i];})
                            end
                        end else if(ics_item.sub_cmd === simple_ics_seq_item::TCH) begin
                            `uvm_do_with(uart_item, {char=={0,ics_item.data[13:7]};})
                            `uvm_do_with(uart_item, {char=={0,ics_item.data[ 6:0]};})
                        end else begin
                            `uvm_do_with(uart_item, {char==ics_item.data[7:0];})
                        end
                    end
                simple_ics_seq_item::WRITE_R:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::WRITE_R,id};})
                        `uvm_do_with(uart_item, {char==ics_item.sub_cmd;})
                        if(ics_item.sub_cmd === simple_ics_seq_item::EEPROM) begin
                            //do nothing
                        end else begin
                            `uvm_do_with(uart_item, {char==ics_item.data[7:0];})
                        end
                    end
                simple_ics_seq_item::ID_R:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::ID,id};})
                    end
                simple_ics_seq_item::POSITION:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::POSITION,id};})
                        `uvm_do_with(uart_item, {char=={0,ics_item.data[13:7]};})
                        `uvm_do_with(uart_item, {char=={0,ics_item.data[ 6:0]};})
                    end
                simple_ics_seq_item::READ:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::READ,id};})
                        `uvm_do_with(uart_item, {char==ics_item.sub_cmd;})
                    end
                simple_ics_seq_item::WRITE:
                    begin
                        `uvm_do_with(uart_item, {char=={simple_ics_seq_item::WRITE,id};})
                        `uvm_do_with(uart_item, {char==ics_item.sub_cmd;})
                        if(ics_item.sub_cmd === simple_ics_seq_item::EEPROM) begin
                            for(int i = 0; i < 64; ++i) begin
                                `uvm_do_with(uart_item, {char==ics_item.eeprom_data[i];})
                            end
                        end else begin
                            `uvm_do_with(uart_item, {char==ics_item.data[7:0];})
                        end
                    end
                simple_ics_seq_item::ID:
                    begin
                        if(ics_item.id_sub_cmd === simple_ics_seq_item::ID_READ) begin
                            `uvm_do_with(uart_item, {char==8'hff;})
                        end else begin
                            `uvm_do_with(uart_item, {char=={simple_ics_seq_item::ID,id};})
                        end
                        for(int i = 0; i < 3; ++i) begin
                            `uvm_do_with(uart_item, {char==ics_item.id_sub_cmd;})
                        end
                    end
            endcase
            up_sequencer.item_done();
        end
    endtask

endclass

2020年6月19日金曜日

ICS_IPをつくる〜その1仕様検討

ジオニックテクニクスで使用しているサーボモータのKRS-3204と、KRS-3302をシリアルコントロールする話です。
通信インターフェースのICSに対応したIPが落ちていないのでこれを自作していきます。
せっかくの題材なのでこれまであまり本格的にやっていなかった高位合成を試していくこととします。
とりあえずは仕様を検討していきます。

ICSの仕様はここです。日本語なので楽勝。必要そうな情報は以下の通り。

最大接続数:
 Max 32個(ID:0〜31)
定周期送信データ:
 ポジション設定 14bit(周期0)x32
定周期受信データ:
 現在ポジション 14bit(周期0)x32
 現在電流値 7bit(周期1)x32
 現在温度 7bit(周期2)x32
オンデマンド送信データ:
 ストレッチ 7bit
 スピード 7bit
 電流制限 7bit
 温度制限 7bit
 EEPROM 64byte
オンデマンド受信データ:
 EEPROM 64byte
 (角度取得 14bit => ソフトで初期処理時に定周期送信データのポジション設定に反映する)
 
ポジションと電流値は制御に使いそうなので早い周期で回しておきます。
温度は比較的に急変しにくいと思うので少しゆっくりした周期で取得します。
オンデマンドの送信はそれほど多く発生しないと思われるので、全体で共有します。
ROSのサービスで使われることを想定します。
設定が反映されているかどうかはオンデマンドでEEPROMのデータを取得して確認します。
角度取得は初期化時にオンデマンドで取得できるようにしておきます。
定周期送信データは32ch分を一括で転送できるように連続して配置します。
定周期受信データも一括して取得できるようにポジション、電流値、温度毎に連続して配置します。

とりあえずレジスタマップはこんな感じかな。

offsetnamebitR/Winitial valuedescription
0x0010baud_rate[3:0]R/W0b00000:115200bps
1:625000bps
2:1152000bps
other:禁止


[31:4]R0x0000000RSV
0x0018number_of_servos[7:0]R/W0x01ICSで通信するサーボの数を書くこと


[31:8]R0x000000RSV
0x0020cyclic0_config[15:0]R/W0x000Acyclic0で通信する周期をms単位で指定


[30:16]R0x0000RSV


[31]R/W0b00:cyclic0の通信停止
1:cyclic0の通信開始
0x0028cyclic1_config[15:0]R/W0x000Acyclic1で通信する周期をms単位で指定


[30:16]R0x0000RSV


[31]R/W0b00:cyclic1の通信停止
1:cyclic1の通信開始
0x0030cyclic2_config[15:0]R/W0x03E8cyclic2で通信する周期をms単位で指定


[30:16]R0x0000RSV


[31]R/W0b00:cyclic2の通信停止
1:cyclic2の通信開始
0x0038ondemand_cmd[7:0]R/W0x00送信サイズをバイト単位で指定


[30:8]R0x00RSV


[31]R0b0ondemand通信ステータス
0:通信完了
1:通信中


[31]W0b0ondemand通信コマンド
0:変化なし
1:通信指示
0x0040RSV[31:0]R/W0x00000000RSV
︙




0x07FCRSV[31:0]R/W0x00000000RSV
0x0800cyclic0_tx_0[7:5]R/W0b100ポジション設定コマンド(0b100)を書くこと


[4:0]R/W0b00000対象となるサーボのIDを書くこと


[15:8]R/W0x00RSV


[31:16]R/W0x0000設定舵角14bit(bit31,bit30は0固定)
0x0804cyclic0_tx_1[31:0]R/W0x00000080cyclic0_tx_0同様
0x0808cyclic0_tx_2[31:0]R/W0x00000080cyclic0_tx_0同様
︙




0x087Ccyclic0_tx_31[31:0]R/W0x00000080cyclic0_tx_0同様
0x0880cyclic1_tx_0[7:5]R/W0b10115


[4:0]R/W0b00000対象となるサーボのIDを書くこと


[15:8]R/W0x03サブコマンドとして電流値を指定すること(0x03)


[23:16]R/W0x00RSV


[31:24]R/W0x00RSV
0x0884cyclic1_tx_1[31:0]R/W0x000003A0cyclic1_tx_0同様
︙




0x08FCcyclic1_tx_31[31:0]R/W0x000003A0cyclic1_tx_0同様
0x0900cyclic2_tx_0[7:5]R/W0b101パラメータ読み出しコマンド(0b101)を書くこと


[4:0]R/W0b00000


[15:8]R/W0x04サブコマンドとして温度を指定すること(0x04)


[23:16]R/W0x00RSV


[31:24]R/W0x00RSV
0x0904cyclic2_tx_1[31:0]R/W0x000004A0cyclic2_tx_0同様
︙




0x097Ccyclic2_tx_31[31:0]R/W0x000004A0cyclic2_tx_0同様
0x0980ondemand_tx_0_wd0[7:5]R/W0b000ICSの任意のコマンドを指定すること


[4:0]R/W0b00000対象のIDを記載すること


[15:8]R/W0x00ICSの2byte目を書くこと


[23:16]R/W0x00ICSの3byte目を書くこと


[31:24]R/W0x00ICSの4byte目を書くこと
0x0984ondemand_tx_0_wd1[7:0]R/W0x00ICSの5byte目を書くこと


[15:8]R/W0x00ICSの6byte目を書くこと


[23:16]R/W0x00ICSの7byte目を書くこと


[31:24]R/W0x00ICSの8byte目を書くこと
︙




0x09C0ondemand_tx_0_wd17[7:0]R/W0x00ICSの65byte目を書くこと


[15:8]R/W0x00ICSの66byte目を書くこと


[23:16]R/W0x00RSV


[31:24]R/W0x00RSV
0x09C4RSV[31:0]R/W0x00RSV
︙




0x0C00cyclic0_rx_0[7:5]R/W0x0000cyclic0_tx_0に対応する返信コマンド(0b000)


[4:0]R/W0b00cyclic0_tx_0に対応する返信データに記載されているサーボID


[15:8]R/W0x00受信カウンタ


[31:16]R/W0x00現在角度の取得結果。通信データと異なり14bitのデータに復元する
0x0C04cyclic0_rx_1[31:0]R/W0x00000000cyclic0_rx_0同様
︙




0x0C7Ccyclic0_rx_31[31:0]R/W0x00000000cyclic0_rx_0同様
0x0C80cyclic1_rx_0[7:0]R/W0x00cyclic1_tx_0に対応した返信コマンドとIDが保存される


[15:8]R/W0x00cyclic1_tx_0に対応したサブコマンドが保存される


[23:16]R/W0x00読み出した電流値が保存される


[31:24]R/W0x00受信カウンタ
0x0C84cyclic1_rx_1[31:0]R/W0x00000000cyclic1_rx_0同様
︙




0x0CFCcyclic1_rx_31[31:0]R/W0x00000000cyclic1_rx_0同様
0x0D00cyclic2_rx_0[7:0]R/W0x00cyclic1_tx_0に対応した返信コマンドとIDが保存される


[15:8]R/W0x00cyclic1_tx_0に対応したサブコマンドが保存される


[23:16]R/W0x00読み出した温度値が保存される


[31:24]R/W0x00受信カウンタ
0x0D04cyclic2_rx_1[31:0]R/W0x00000000cyclic2_rx_0同様
︙




0x0D7Ccyclic2_rx_31[31:0]R/W0x00000000cyclic2_rx_0同様
0x0D80ondemand_rx_0_wd0[7:5]R/W0b000ondemand_tx_0_wd0に対応した返信コマンドが保存される


[4:0]R/W0b00000ondemand_tx_0_wd0に対応した返信コマンドのIDが保存される


[15:8]R/W0x00ondemand_tx_0の返信データ2byte目が保存される


[23:16]R/W0x00ondemand_tx_0の返信データ3byte目が保存される


[31:24]R/W0x00ondemand_tx_0の返信データ4byte目が保存される
︙




0x0DC0ondemand_rx_0_wd17[7:0]R/W0x00ondemand_tx_0の返信データ65byte目が保存される


[15:8]R/W0x00ondemand_tx_0の返信データ66byte目が保存される


[23:16]R/W0x00RSV


[31:24]R/W0x00受信カウンタ
0x0DC4RSV[31:0]R/W0x00000000RSV
︙




0x0FFCRSV[31:0]R/W0x00000000RSV