OpenCores
URL https://opencores.org/ocsvn/openhmc/openhmc/trunk

Subversion Repositories openhmc

[/] [openhmc/] [trunk/] [openHMC/] [sim/] [UVC/] [hmc/] [sv/] [hmc_driver_base.sv] - Rev 15

Compare with Previous | Blame | View Log

/*
 *                              .--------------. .----------------. .------------.
 *                             | .------------. | .--------------. | .----------. |
 *                             | | ____  ____ | | | ____    ____ | | |   ______ | |
 *                             | ||_   ||   _|| | ||_   \  /   _|| | | .' ___  || |
 *       ___  _ __   ___ _ __  | |  | |__| |  | | |  |   \/   |  | | |/ .'   \_|| |
 *      / _ \| '_ \ / _ \ '_ \ | |  |  __  |  | | |  | |\  /| |  | | || |       | |
 *       (_) | |_) |  __/ | | || | _| |  | |_ | | | _| |_\/_| |_ | | |\ `.___.'\| |
 *      \___/| .__/ \___|_| |_|| ||____||____|| | ||_____||_____|| | | `._____.'| |
 *           | |               | |            | | |              | | |          | |
 *           |_|               | '------------' | '--------------' | '----------' |
 *                              '--------------' '----------------' '------------'
 *
 *  openHMC - An Open Source Hybrid Memory Cube Controller
 *  (C) Copyright 2014 Computer Architecture Group - University of Heidelberg
 *  www.ziti.uni-heidelberg.de
 *  B6, 26
 *  68159 Mannheim
 *  Germany
 *
 *  Contact: openhmc@ziti.uni-heidelberg.de
 *  http://ra.ziti.uni-heidelberg.de/openhmc
 *
 *   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 3 of the License, or
 *   (at your option) any later version.
 *
 *   This source file 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 file.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */
`ifndef HMC_DRIVER_BASE_SV
`define HMC_DRIVER_BASE_SV

class hmc_driver_base#(parameter NUM_LANES = 16) extends uvm_driver #(hmc_packet);


        `uvm_analysis_imp_decl(_hmc_frp)
        uvm_analysis_imp_hmc_frp #(hmc_packet, hmc_driver_base#(.NUM_LANES(NUM_LANES))) hmc_frp_port;

        init_state_t next_state = RESET;
        init_state_t state = RESET;
        init_state_t last_state = LINK_UP;

        virtual interface hmc_sr_if #(.NUM_LANES(NUM_LANES)) vif;

        hmc_token_handler token_handler;
        hmc_retry_buffer  retry_buffer;
        hmc_link_status   remote_status;

        //-- The parameters for this link
        hmc_link_config link_config;

        //-- The parameters for this driver
        hmc_local_link_config local_config;

        //-- Packets to send
        hmc_packet packet_queue[$];

        typedef bit lane_queue [$];
        lane_queue lane_queues [NUM_LANES];     

        event driver_clk;

        uvm_event start_clear_retry_event;

        // Timestamps for debugging
        int reset_timestamp = 0;
        int prbs_timestamp = 0;
        int null_timestamp = 0;
        int req_ts1_timestamp = 0;
        int ts1_timestamp = 0;

        // Timestamps for controlling packet flow
        int last_packet_timestamp = 0;
        int start_retry_timestamp = 0;
        //-- error propability
        int next_poisoned = 0;
        int     lng_error_prob = 0;
        int     seq_error_prob = 0;
        int     crc_error_prob = 0;

        bit recover_from_power_down = 0;

        // Count retry attempts signalled by the responder (from here)
        int retry_attempts = 0;

        // Count retry attempts from the requester
        int remote_retries_signalled = 0;
        int remote_retries_cleared = 0;
        int local_retries_signalled = 0;
        int local_retries_cleared = 0;

        // Internal state for scramblers
        bit [14:0] lfsr[NUM_LANES-1:0];

        // Internal state for packets
        bit [2:0] seq_num = 1;

        // State for tokens and frp
        bit [7:0] frp_queue [$];
        bit [7:0] last_frp = 0;
        bit [7:0] last_rrp;

        int tokens_to_send = 0;

        int used_tokens = 0;
        int sent_tokens = 0;

        bit [14:0] lfsr_seed[0:15] = {
                                        15'h4D56,
                                        15'h47FF,
                                        15'h75B8,
                                        15'h1E18,
                                        15'h2E10,
                                        15'h3EB2,
                                        15'h4302,
                                        15'h1380,
                                        15'h3EB3,
                                        15'h2769,
                                        15'h4580,
                                        15'h5665,
                                        15'h6318,
                                        15'h6014,
                                        15'h077B,
                                        15'h261F
                        };

        bit [15:8]      ts1_high                = 8'hF0;
        bit [7:4]       ts1_top_lane    = 4'hC;
        bit [7:4]       ts1_bottom_lane = 4'h3;
        bit [7:4]       ts1_middle_lane = 4'h5;

        // Configuration parameters
        bit init_continue;
        bit can_continue;

        `uvm_component_param_utils(hmc_driver_base #(.NUM_LANES(NUM_LANES)))

        function new(string name="hmc_driver_base", uvm_component parent);
                super.new(name,parent);
        endfunction : new

        function void build_phase(uvm_phase phase);
                super.build_phase(phase);

                vif_found : assert (uvm_config_db#(virtual interface hmc_sr_if#(.NUM_LANES(NUM_LANES)))::get(this, "", "vif",this.vif));
                config_found : assert (uvm_config_db#(hmc_link_config)::get(this, "", "link_config",link_config));
        endfunction : build_phase

        virtual task run_phase(uvm_phase phase);
                super.run_phase(phase);
        endtask : run_phase

        task reset();

                vif.TXP = {$size(vif.TXP) {1'bz}};
                vif.TXN = {$size(vif.TXN) {1'bz}};
                vif.TXPS = 1'bz;

                seq_num = 1;
                last_rrp = 0;
                init_continue = 0;
                can_continue = 0;
                retry_buffer.reset();

                //-- wait for reset signal
                @(posedge vif.P_RST_N);
                reset_timestamp = $time;

                next_state = INIT;
        endtask : reset

        task clk_gen();
                @(posedge vif.REFCLKP);
                forever begin
                        #link_config.bit_time -> driver_clk;
                end
        endtask : clk_gen

        task send_ts1(int ts1_fits);
                bit [4:0] ts1_seq_num;
                bit [NUM_LANES-1:0] fit_val;
                bit [15:0] ts1_values [NUM_LANES-1:0];

                ts1_values[0]           = {ts1_high, ts1_bottom_lane, 4'h0};
                for (int lane=1; lane < local_config.width-1; lane++)
                        ts1_values[lane]        = {ts1_high, ts1_middle_lane, 4'h0};
                ts1_values[local_config.width-1]        = {ts1_high, ts1_top_lane, 4'h0};

                //Send some (possibly incomplete) TS1 flits
                while (ts1_fits > 0) begin
                        // Cycle through all the sequence numbers
                        for (ts1_seq_num=0; ts1_seq_num < 16 && ts1_fits > 0; ts1_seq_num++) begin
                                // Add the sequence number to the ts1_values
                                for (int i=0; i < local_config.width; i++) begin
                                        ts1_values[i][3:0] = ts1_seq_num;
                                end
                                // Send the fits of the ts1 values
                                for (int fit=0; fit < 16; fit++) begin
                                        for (int lane=0; lane < local_config.width; lane++) begin
                                                fit_val[lane] = ts1_values[lane][fit];
                                        end
                                        if (ts1_fits > 0) begin
                                                drive_fit(fit_val);
                                        end else begin
                                                drive_fit({NUM_LANES{1'b0}});
                                        end
                                        ts1_fits = ts1_fits - 1;
                                end
                        end
                end
        endtask : send_ts1

        task initial_trets();
                hmc_packet tret = new();
                //send TRET FLITs
                while (tokens_to_send > 0) begin
                        init_tret_randomization : assert (tret.randomize() with {
                                command == HMC_TRET;
                                poisoned == 0;
                                crc_error == 0;
                                packet_length == 1;
                                duplicate_length == 1;
                                return_token_count <= tokens_to_send && return_token_count > 0;
                                });
                        send_packet(tret);
                        tokens_to_send = tokens_to_send - tret.return_token_count;
                end
                next_state = LINK_UP;
        endtask : initial_trets

        task link_up();
                hmc_packet packet;

                get_packets();

                if (packet_queue.size() > 0 && token_handler.tokens_available(packet_queue[0].packet_length) && (250- retry_buffer.get_buffer_used()) > packet_queue[0].packet_length ) begin
                        packet = packet_queue.pop_front();      //-- send the first packet in the queue
                        if ((next_poisoned < local_config.poisoned_probability))
                                send_poisoned(packet);
                        else
                                send_packet(packet);

                        poisoned_propability_randomisation : assert (std::randomize(next_poisoned) with {next_poisoned > 0 && next_poisoned < 1000;});

                end else if ($time-last_packet_timestamp > local_config.send_pret_time && frp_queue.size() > 0) begin
                        `uvm_info(get_type_name(),$psprintf("sending pret, frp_queue size = %0d", frp_queue.size()), UVM_HIGH)
                        send_pret();
                end else if ($time-last_packet_timestamp > local_config.send_tret_time && (used_tokens - sent_tokens) > 0 &&(250- retry_buffer.get_buffer_used()) >1) begin
                        `uvm_info(get_type_name(),$psprintf("sending tret, (<%0d)", used_tokens-sent_tokens), UVM_HIGH)
                        send_tret();
                end else begin
                        drive_flit(128'h0);
                end

                // From here on, there are no packets being driven.  This is just logic to decide the next state.

                //-- Handle error_abort_mode on remote link 
                if (remote_status.get_error_abort_mode() && $time() - start_retry_timestamp > link_config.retry_timeout_period  ) begin
                        next_state = START_RETRY_INIT;
                end
        endtask : link_up

        task start_retry_init();        //-- send start retry IRTRYs

                start_retry_timestamp = $time;
                local_retries_signalled = local_retries_signalled + 1;
                `uvm_info(get_type_name(),$psprintf("sending start retry packets"), UVM_MEDIUM)

                //send IRTRY FLITs
                for (int i=0; i < local_config.irtry_flit_count_to_send; i++)
                        send_irtry_start();

                next_state = LINK_UP;
        endtask : start_retry_init

        task clear_retry(); //-- send clear error abort mode IRTRYs

                local_retries_cleared = local_retries_cleared + 1;
                //send IRTRY FLITs
                for (int i=0; i < local_config.irtry_flit_count_to_send; i++)
                        send_irtry_clear();
                next_state = SEND_RETRY_PACKETS;
        endtask : clear_retry

        task send_retry_packets();
                hmc_packet packet;
                int spacer_flits;

                packet = retry_buffer.get_retry_packet();
                while (packet != null) begin
                        spacer_flits_randomization_succeeds : assert (std::randomize(spacer_flits) with {spacer_flits >= 0 && spacer_flits < 10;});
                        for (int i=0; i<spacer_flits; i++) begin
                                drive_flit(128'h0);
                        end
                        retry_send_packet(packet);
                        packet = retry_buffer.get_retry_packet();
                end
                next_state = LINK_UP;
        endtask : send_retry_packets

        task get_packets();
                hmc_packet packet;

                if( seq_item_port.has_do_available() ) begin
                        if( packet_queue.size() == 0) begin
                                seq_item_port.get_next_item(packet);
                                packet_queue.push_back(packet);
                                seq_item_port.item_done();
                        end
                end
        endtask : get_packets

        task send_irtry_start();
                send_irtry(1,0);
        endtask : send_irtry_start

        task send_irtry_clear();
                send_irtry(0,1);
        endtask : send_irtry_clear

        task send_irtry(input bit start, input bit clear);
                hmc_packet irtry = new();

                irtry_randomization: assert (irtry.randomize() with {
                        command == HMC_IRTRY;
                        packet_length == 1;
                        duplicate_length == 1;
                        start_retry == start;
                        clear_error_abort == clear;
                });

                send_packet(irtry);
        endtask : send_irtry

        task send_tret();

                hmc_packet tret = new();

                tret_randomization: assert (tret.randomize() with {
                        command == HMC_TRET;
                        packet_length == 1;
                        duplicate_length == 1;
                });

                send_packet(tret);

        endtask : send_tret

        task send_pret();

                hmc_packet pret = new();

                pret_randomization: assert (pret.randomize() with {
                        command == HMC_PRET;
                        packet_length == 1;
                        duplicate_length == 1;
                });

                send_packet(pret);

        endtask : send_pret

        task send_poisoned(input hmc_packet pkt);               
                hmc_packet poisoned = new pkt; //-- copy the pkt into a new one 
                
                `uvm_info(get_type_name(),$psprintf("Poisoning Packet with command %s and tag %d", poisoned.command.name(), poisoned.tag),UVM_NONE)

                poisoned.poisoned = 1;
                send_packet(poisoned);
                
                packet_queue.push_back(pkt);//-- resent the packet later

        endtask : send_poisoned

        task send_packet(input hmc_packet pkt);
                int packet_frp;
                bit [31:0] crc;
                int bit_pos;
                int tok_cnt;
                // Save packet in Retry buffer if not IRTRY, NULL, or PRET
                // Tokens and Sequence numbers are saved in the retry buffer.
                
                if (pkt.command == HMC_IRTRY ||
                        pkt.command == HMC_NULL ||
                        pkt.command == HMC_PRET) begin
                        packet_frp = 0;
                        pkt.sequence_number = 0;
                        retry_send_packet(pkt);
                end else begin
                        pkt.sequence_number = seq_num;
                        if (state != INITIAL_TRETS) begin
                                if (used_tokens - sent_tokens > 0) begin
                                        // Always send tokens with TRETs
                                        if (pkt.command == HMC_TRET)
                                                tok_cnd_tret_randomization : assert (std::randomize(tok_cnt) with {(pkt.command == HMC_TRET && tok_cnt > 0)  && tok_cnt < 32 && tok_cnt <= (used_tokens - sent_tokens);}); 
                                        else
                                                tok_cnt_randomization_succeeds : assert (std::randomize(tok_cnt) with {(tok_cnt >= 0) && tok_cnt < 32 && tok_cnt <= (used_tokens - sent_tokens);});
                                        pkt.return_token_count = tok_cnt;
                                end else begin
                                        pkt.return_token_count = 0;
                                end
                        end
                        packet_frp = retry_buffer.add_packet(pkt);
                        if(packet_frp != -1)begin
                                seq_num++;
                                if (state != INITIAL_TRETS) begin
                                        sent_tokens += pkt.return_token_count;
                                end
                                // From now on, it's equivalent to retry
                                `uvm_info(get_type_name(), $psprintf("Sending CDM  %s with TRETS %d", pkt.command.name(), pkt.return_token_count), UVM_HIGH)
                                retry_send_packet(pkt);
                        end
                end
        endtask : send_packet

        task retry_send_packet(input hmc_packet pkt);
                bit [31:0] crc;
                int bit_pos;
                int bit_error;
                int error_type;
                int rrp_to_send;
                
                
                int pkt_lng;
                bit [2:0]seq_number;
        

                // Don't change the stored packet (except to clear the CRC error flag)
                hmc_packet copy = new pkt;
                
                // Return retry pointers are not saved.
                // Skip some retry pointers
                rrp_to_send_randomization_succeeds : assert (std::randomize(rrp_to_send) with {rrp_to_send >= 0 && rrp_to_send <= frp_queue.size();});
                if (rrp_to_send == 0) begin
                        copy.return_retry_pointer = last_rrp;
                end else begin
                        for (int i=0;i<rrp_to_send;i++) begin
                                copy.return_retry_pointer = frp_queue.pop_front();
                                `uvm_info(get_type_name(),$psprintf("popped %0d for frp in %s", copy.return_retry_pointer, copy.command.name()),UVM_HIGH)
                        end
                end
                last_rrp = copy.return_retry_pointer;

                //-- ERROR INJECTION

                //-- adding sequence error
                error_type_seq_error_randomization : assert (std::randomize(seq_error_prob) with {seq_error_prob > 0 && seq_error_prob < 1000;});
                if (seq_error_prob < local_config.seq_error_probability) begin
                                randcase
                                        1:      copy.sequence_number = copy.sequence_number + 1;
                                        1:      copy.sequence_number = copy.sequence_number - 1;
                                        1:      begin
                                                        random_SEQ_succeeds : assert(
                                                                        std::randomize(seq_number) with { seq_number !=copy.sequence_number;}); 
                                                        copy.sequence_number = seq_number; 
                                                end
                                endcase
                                `uvm_info(get_type_name(),$psprintf("injecting SEQ error in CMD %s and FRP %d",copy.command.name(), copy.forward_retry_pointer), UVM_NONE)
                end

                        
                //-- inserting Length Error
                error_type_lng_error_randomization : assert (std::randomize(lng_error_prob) with {lng_error_prob > 0 && lng_error_prob < 1000;});
                if (lng_error_prob < local_config.lng_error_probability) begin
                        randcase
                                1:      copy.packet_length = copy.packet_length + 1;
                                1:      copy.packet_length = copy.packet_length - 1;
                                1:      copy.duplicate_length = copy.duplicate_length + 1;
                                1:      copy.duplicate_length = copy.duplicate_length - 1;
                                1:      begin   random_LNG_succeeds : assert(
                                                                std::randomize(pkt_lng) with {pkt_lng >= 0 && pkt_lng < 16  && pkt_lng !=copy.packet_length;}); 
                                                        copy.packet_length = pkt_lng; 
                                        end
                                1:      begin   random_DLN_succeeds : assert(
                                                                std::randomize(pkt_lng) with {pkt_lng >= 0 && pkt_lng < 16  && pkt_lng !=copy.duplicate_length;});
                                                        copy.duplicate_length = pkt_lng; 
                                        end
                        endcase
                end
                        
                crc = copy.calculate_crc(); //-- calculate crc for packet with valid tail
                
                
                //-- poisoning packets
                if (copy.poisoned) begin
                        `uvm_info(get_type_name(),$psprintf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX poisoning packet with CRC %0x and CMD %s and TAG %d", ~crc, copy.command.name(), copy.tag),UVM_LOW)
                        crc = ~crc;
                end


                //-- inserting CRC error
                error_type_crc_error_randomization : assert (std::randomize(crc_error_prob) with {crc_error_prob > 0 && crc_error_prob < 1000;});
                if (crc_error_prob < local_config.crc_error_probability * copy.packet_length) begin //-- increase the crc error probability for longer packets
                        int clear_crc_error;
                        crc_bit_error_randomization_succeeds : assert (std::randomize(bit_pos) with {bit_pos >= 0 && bit_pos < 32;});
                        `uvm_info(get_type_name(),$psprintf("inserting crc error at %0x", bit_pos),UVM_LOW)
                        crc[bit_pos] = !crc[bit_pos];
                        clear_crc_error_randomization_succeeds : assert (std::randomize(clear_crc_error) with {clear_crc_error >= 0 && clear_crc_error < 3;});
                        if (clear_crc_error == 1)
                                pkt.crc_error = 0;
                end
                copy.packet_crc = crc;
                drive_tx_packet(copy);

        endtask : retry_send_packet

        task drive_fit(input bit[NUM_LANES-1:0] new_value);
                if (link_config.scramblers_enabled) begin
                        drive_lanes(get_scrambler_value()^new_value);
                end else begin 
                        drive_lanes(new_value);
                end
                @driver_clk;
                step_scramblers();
        endtask : drive_fit

        task drive_flit(input bit [127:0] flit);
                int i;
                bit [15:0] fits [16];

                for (int i=0; i<128; i++)
                        fits[i/local_config.width][i%local_config.width] = flit[i];

                for (int i=0; i<128/local_config.width;i++) begin
                        drive_fit(fits[i]);
                end

        endtask : drive_flit

        task drive_tx_packet(input hmc_packet pkt);
                bit bitstream[];
                bit [127:0] flits [9]; // Max packet size is 9 flits
                bit [127:0] curr_flit;
                int i;
                int bitcount;
                bitcount = pkt.pack(bitstream);

                for (int i=0; i<bitcount; i++)
                        flits[i/128][i%128] = bitstream[i];
                for (int i=0; i<bitcount/128; i++) begin
                        drive_flit(flits[i]);
                end

                last_packet_timestamp = $time;

        endtask : drive_tx_packet

        virtual function void drive_lanes(input bit[NUM_LANES-1:0] new_value);
                `uvm_info(get_type_name(),$psprintf("called virtual function drive_lanes!"), UVM_HIGH)
        endfunction : drive_lanes

        function void reset_lfsr();
                int i;

                for (i = 0; i < NUM_LANES; i= i+1) begin
                        if (local_config.reverse_lanes == 1)
                                lfsr[i] = lfsr_seed[NUM_LANES-1-i];
                        else
                                lfsr[i] = lfsr_seed[i];
                end
        endfunction : reset_lfsr

        function void step_scramblers();
                int i;

                if (link_config.scramblers_enabled) begin
                        for (i = 0; i < NUM_LANES; i= i+1) begin
                                lfsr[i] = {lfsr[i][1]^lfsr[i][0], lfsr[i][14:1]};
                        end
                end
        endfunction : step_scramblers

        function bit[NUM_LANES-1:0] get_scrambler_value();
                int i;

                if (link_config.scramblers_enabled) begin
                        for (i = 0; i < NUM_LANES; i= i+1) begin
                                get_scrambler_value[i] = lfsr[i][0];
                        end
                end else begin
                        for (i = 0; i < NUM_LANES; i= i+1) begin
                                get_scrambler_value = {NUM_LANES {1'b0}};
                        end
                end
        endfunction : get_scrambler_value

        // I2C or JTAG would configure the HMC during reset
        function void set_init_continue();
                tb_respects_tINIT : assert (can_continue);
                init_continue = 1;
        endfunction : set_init_continue

        virtual function void write_hmc_frp(input hmc_packet pkt);

                bit [7:0] frp;
                int random_wait;

                `uvm_info(get_type_name(),$psprintf("hmc_frp: %s with FRP %0d & size %0d",pkt.command.name(), pkt.forward_retry_pointer, pkt.packet_length),UVM_HIGH)

                // IRTRY and PRET do not have valid frp fields
                if (pkt.command != HMC_IRTRY && pkt.command != HMC_PRET) begin
                        frp = pkt.forward_retry_pointer;
                        if (frp != last_frp) begin
                                frp_queue.push_back(frp);
                                last_frp = frp;
                        end
                end

                if (pkt.get_command_type != HMC_FLOW_TYPE && !pkt.poisoned ) begin
                        used_tokens_does_not_overflow : assert ( used_tokens < used_tokens + pkt.packet_length);
                        used_tokens = used_tokens + pkt.packet_length;
                end

        endfunction : write_hmc_frp

endclass : hmc_driver_base

`endif // HMC_DRIVER_BASE_SV

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.