基于 FPGA 的 DE1-SoC 功率估算器

Introduction

功耗是当今许多技术都要考虑的重要因素。例如,手机生产商总是谈论他们在电源管理方面的改进,以及如何延长电池的使用寿命。功能与功耗之间的平衡是许多人都在研究的有趣课题。然而,当我们做实验时,我们很少会考虑我们消耗了多少电能,因为我们从来没有受到电能的限制。在本课实验 3 中合成巨型鼓时,我们耗尽了 FPGA 上的所有 DSP 块,并使用了大量 M10K 内存。这种对硬件的大量使用很可能会导致高功耗,我们很想知道有哪些因素会影响 FPGA 的功耗。带着这个目标,我们构建了一个电路,使用基于 I2C 的电流传感器 MIKROE-2987 测量流经电源电缆的电流,同时假设有稳定的 12V 电压供应,并在 FPGA 中实施了一个带积分器的 I2C 接收器,以持续增加消耗的能量。根据获得的数据,我们在 VGA 上建立了一个直观的用户界面,显示电流、实时功率和平均能量,以监控 DE1-SoC 的功耗。

Design & testing methods

design overview

功率估算器的整体设计如图 1 所示。MIKOE-2987 利用霍尔效应测量通过内部熔断器输入引脚的电流,具有约 1.2m Ω 的极低串联电阻,不会影响提供给 FPGA 的电流。
该电路板使用 I2C 接口(SDA 表示数据,SCL 表示时钟)来传输经过处理的电流数据。根据规范,我们在 FPGA 中构建了 I2C 主站作为接收器,将比特流转换为实际的高分辨率电流数据,并以有效信号传递给 HPS。
该接收器还包含一个乘法器和积分器,分别用于计算实时功率和累积能量。它由 HPS 控制,以实现复位、启动和停止。HPS 将处理数据,并在 VGA 上显示相应的用户界面。为了加快调试,我们还采用了 7 段显示器来显示测量到的电流。

在这里插入图片描述图 1:功率估算器的总体设计

Hardware design

我们都知道物理课上的公式 P=I∗V 表示功率,W=∑tP∗dt 表示能量,这两个简单的公式就是本项目的精髓。根据电源适配器的规格,可提供稳定的 12V 电压。在这种情况下,实时功率通过一个乘法器来计算 I∗12。
至于消耗的功率,我们设计了一个与 Lab1 相同的积分器,在计算开始后,以单位时间 P∗dt 为每个可用采样点增加能量(dt 取决于采样率)。
这个项目的关键在于我们需要一种方法来测量电源适配器提供的电流,以便计算功率和能量。在网上进行了一番研究后,我们决定使用 MIKROE-2987 电流测量板,原因如上所述。
该电流传感器的输出进入一个 12 位 ADC,并通过 MCP3221 的 I2C 接口提供。因此,我们在 FPGA 中建立了 I2C 主接收器,以便与之通信。
MCP3221 的 I2C 协议如图 2 所示。I2C 通信只涉及 2 根导线:SDA 用于数据,SCL 用于时钟。SCL 是单向的,由 I2C 接收器提供,而 SDA 是双向的,可由主设备(I2C 接收器)或从设备(MCP3221)控制。
在这里插入图片描述图 2:MCP3221 指定的 I2C 协议

根据数据手册,主站和从站之间的每个事务长度应为 8 位,并总是在 ACK/NAK 之后。当 MCP3221 不忙时,SDA 和 SCL 都将置高。
要启动数据交易,主设备必须在 SCL 为高电平时将 SDA 线路从高电平拉低到低电平,从而发出启动条件信号。
当芯片被唤醒时,它将接收来自主站的地址字节,以便在 SDA 中配置芯片。前 4 位是设备位,后 3 位是地址位,最后一位是 R/W 选择位。我们将其默认配置为 1001_101_1,以便从芯片读取数据。
当接受到一个有效的地址字节时,芯片会将 SDA 设置为低电平作为 ACK 信号,然后开始提供数据。每次提供的数据以字节为单位。当提供完一个字节后,需要主站发出低电平有效的 ACK 信号,以便从站继续提供数据。
由于数据为 12 位形式,因此需要两个字节(我们称之为上字节和下字节)来完成一个数据交易,上字节的前四位为零。
当主站发出连续 ACK 时,从站将继续提供当前值,而不会重复地址字节。
要终止交易,主站可在 SCL 也为高电平时将 SDA 拉高,从而产生 NAK;在 SCL 为高电平时将 SDA 从低电平拉高,从而以停止条件结束交易。值得注意的是,SDA 线路的变化应始终发生在 SCL 为低电平时,并在时钟的高电平部分保持稳定(启动和停止条件除外),而且每个数据位应有一个时钟脉冲。

根据上述 I2C 协议,我们在 SystemVerilog 中开发了一个 11 状态的 FSM,如图 3 所示。
在这里插入图片描述
在 IDLE 状态下,SDA 和 SCL 都将切换。当检测到来自 HPS 的启动信号时,将提供启动条件,然后传输地址字节。
当内部计数器检测到地址字节已传送完毕时,将对从站进行确认。否则,主站将回到 IDLE 状态。如果提供了 ACK,主站将在收到数据后接收从站的上下字节。
在 STATE_DATA_RECEIVED_UPPER 和 STATE_DATA_RECEIVED_LOWER 状态下,主站从从站接收数据,并将其放入 16 位长的 FIFO 中。
当 HPS 通过拨动停止信号指示停止系统时,将提供 NACK,然后提供停止条件,随后进入等待状态,以确保 MCP3221 不再忙。
值得注意的是,由于 SDA 线路可同时由主设备和从设备控制,我们编写了一个三态缓冲器来决定由哪个设备控制该线路。
三态缓冲器带有一个使能信号,当主设备在事务中充当输入时,该信号为高电平,当该使能信号断言时,SDA 在主设备端应变为高阻抗。
否则,SDA 从主站输出。在不发送启动/停止条件、不等待或不空闲时,SCL 时钟线作为 200K Hz 时钟信号跟随输入。当主站确认数据的较低位或主站发出 NAK 时,数据有效信号被拉为高电平,表明我们可以从 MCP3221 获取正确的值。该信号稍后将用于控制计算累积能量的积分器。

通过 I2C 接收器,我们可以获得 12 位数据,但仍需将其转换为实际电流数据,我们将其设置为 9.23 位定点数据。相应的电压可计算为 d a t a o u t 2 12 ∗ 3.3 \frac{dataout}{2^{12}} * 3.3 212dataout3.3 V。根据规范,电流传感器的灵敏度为 110mv/A,原点为 1.65V,因此我们可以计算出实际电流为 I = V − 1.65 V 110 m V / A I= \frac{V-1.65V}{110mV/A} I=110mV/AV1.65V

MCP3221 I2C 接口还规定它能达到的最大时钟频率为 400kHz,我们选择 200KHz 应该是一个合理的选择。
这意味着 50MHz 的 FPGA 时钟速度过快,无法运行,因此我们增加了一个时钟分频器,将其降到 200kHz。
如上所述,我们接着实施了一个积分器,通过在单位时间内不断增加能量来计算累积能量。
我们使用 HPS 发出的停止信号来暂停积分,然后继续启动。只要停止信号未被断言,且 I2C 接口的数据有效,我们就会通过计算 V∗I∗dt 来计算新的单位能量,并将其与消耗的总能量相加。
dt 的值是通过使用 SignalTap 计数两个连续数据有效信号之间的脉冲数确定的。我们计算了 18 个脉冲,与协议相符(16 个脉冲用于传输数据,2 个脉冲用于 ACK)。
这意味着我们在每次新的电流采样时的单位能量为 V ∗ I ∗ 18 200000 \frac{V∗I∗18}{200000} 200000VI18。尽管输入电流使用的是 7.23 定点值,但我们的积分器模块的能量输出被设置为 41.23 定点,即 64 位长数据,因为能量是累积的,所以需要更多位数来防止溢出。
但是,Qsys PIO 的最大位宽只有 32 位,因此我们必须创建两个 PIO 端口,一个用于传输较高的 32 位,另一个用于传输较低的 32 位。然后,我们使用 long long 类型变量在 HPS 中存储 64 位值,之后通过除法 223 将其从定点转换为双倍。位数增加一倍也提高了读数精度。
每当电流有效时,我们也会递增一个计数器,通过计算 (cycle_count - 1)*KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …rac{18}{200000] 就能确定 FPGA 开始运行后所经过的时间。这里我们不考虑第一个事务的时间,即不计算前 27 个周期,因为其中包括传输地址比特的时间,而这并不代表中间的事务。

为了使调试更加直观,我们在 FPGA 上实现了一个简单的段解码器来显示测量的电流值。如上所述,电流值为 9.23 固定点,我们使用分段显示器 0 作为符号,后面两位作为整数,其余作为分数部分。我们建立了一个映射表,将前 9 位作为整数,小数点后 23 位中的 4 位作为分数。

Software design

所有需要的硬件都已实例化,我们开始添加与 HPS 的通信。由于我们的目标是在 VGA 显示屏上以图形方式显示数据,因此从软件方面着手更为方便。
为了保证软件的正确性,我们在设计软件时采用了渐进式方法。首先,我们通过在 Qsys 总线上添加 PIO,允许数据从 FPGA 流向 HPS。
每个 PIO 都有一个来自轻量级 AXI 主站的偏移量,我们可以通过查看地址:偏移量 + 轻量级 AXI 主站地址来访问数据。
由于积分器模块的输出是能量而不是功率,我们需要进行一些转换。每个给定实例的动态功率等于瞬时电流 * 12V,平均功率等于累积能量/总耗时。在实际绘制这些数据之前,我们要确保我们的数据是有意义的,因此每当数据有效信号被拉到高电平时,我们就会在串行监视器上打印电流值、动态功率、平均功率、周期计数和时间。
在这个过程中,我们意识到需要有意让程序休眠一段时间,因为 HPS 的运行速度比 FPGA 快得多,所以数据有效信号的一个高电平部分将对应 HPS 的数百个周期,从而产生比需要多得多的数据点。
在确认数据正确无误后,我们需要一种适当的方式来显示数据。我们决定在 VGA 上显示三个图表:电流与时间、动态功率与时间、平均功率与时间。
我们将屏幕上的时间刻度限制为 3 秒,这意味着每隔 3 秒就会擦除图表并使用新数据重新绘制。Y 轴刻度由变量决定。就电流而言,它受限于我们电流传感器的能力,该传感器最多可处理 2 安培的电流。对于动态功率和平均功率,最大值为 IMax∗VMax=2∗12=24W。
因此,从 y 轴的底部开始,每增加一个像素,功率就增加 24 119 \frac{24}{119} 11924 瓦。我们还在屏幕右侧创建了一个文本框,用于显示这些变量的实际值。最终的 VGA 显示界面如图 4 所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述图 4:在 VGA 上显示图表
在 VGA 上绘制数据后,我们开始将更多的控制权转移到 HPS 端。我们首先禁用了启动、停止和校准信号的按钮控制,采用了纯软件控制。
这就需要一个能检测键盘输入的用户界面,我们通过使用多个 pthreads 实现了这一点。用户可以在 1-3 之间选择一个数字,每个数字分别对应按下重置、停止或校准键。当检测到停止时,绘图会立即停止,数据显示也会冻结,而启动则会恢复绘图。
另一方面,校准会清除能量和时间数据,并从 0 重新开始。选择校准后,所有图形都将清除并立即开始重新绘制。下面的视频演示了校准的效果。

Testing strategy

为了确保设计中的所有模块都能按预期工作,我们采用了分层测试方法。在最底层,我们首先编写了一个测试平台,根据上述 I2C 协议输入控制信号、时钟和数据,测试 I2C 状态机,并在 ModelSim 中验证输出波形。
如图 5 所示,主站能够发送正确的地址位,在适当的时候生成 ACK,读取输入数据,并在 12 位事务完成时将数据有效信号拉高。
在这里插入图片描述图 5:用于 I2C 主站实施的测试平台的 Modelsim 波形
在仿真中证明 I2C 接口功能正常后,我们在 Quartus 中实例化了模块,并使用 SignalTap 验证了来自 FPGA 的信号反馈。SignalTap 在 FPGA 中创建了真正的硬件电路,并提供了电路产生的实际输出波形。
在使用 SignalTap 时,我们使用实验室的电流源直接向电流传感器馈送电流,因此我们清楚地知道电流输出的预期值。我们还使用 FPGA 上的按钮来控制复位、启动和停止信号,因此可以轻松触发 SignalTap 的数据捕获。通过比较电源上的电流输出和 SignalTap 的数据输出,我们可以知道我们的实现是否正确。

在项目的最后,我们不再使用电流源,而是直接测量 FPGA 的电流。我们在面包板上放置了一个 2.1 毫米内螺纹直流电源插孔,并将 FPGA 电源适配器连接到该插孔。
使用跳线将电源插孔的电源引脚连接到 mikroe-2987 电路板的 P+ 引脚,电流将通过电路板到达其 P- 引脚,我们将另一根跳线连接到公头插孔至开口电源线的正端。
公电源插孔插入 FPGA,而其负端则连接回面包板上母电源插孔的接地引脚。
这种设置形成了一个串联电路,其中电流传感器测量的电流应与 FPGA 的电流相同。图 6 和图 7 显示了我们如何设置这个串联电路。我们观察屏幕上的图形和不同读数,以确定数据是否合理。
在这种设置下,我们观察到电流和功率读数相当稳定,这在意料之中,因为 FPGA 始终在运行相同的程序。在这一阶段,我们还改用纯软件控制而非按钮,并将 VGA 输出在软件中发出信号时的行为与预期进行了比较。
我们可以看到在选择停止时图形停止,而在开始时又恢复绘制。校准工作也正常进行,因为图形被擦除,时间轴被正确重置,电流和能量的数据显示回到 0。

在这里插入图片描述
图 6:直接测量 FPGA 电流的电路设置
在这里插入图片描述
图 7:近距离观察电流传感器周围的电路

Results & Analysis

在对系统进行上述进一步验证后,我们能够利用它来探索 FPGA 中一些有趣的功耗行为。根据我们在英特尔社区中发现的一些讨论,以太网calble会导致大量能耗。我们的系统可以很好地证明这一点。因此,我们选择使用串口来控制 FPGA。我们首先在插入以太网电缆的情况下运行功率估算器。如图 8 所示,这种情况下的平均功率约为 7.72W。
然后,我们断开以太网电缆,通过断定串行接口的信号来校准读数。如图 9 所示,平均功率立即下降到 6.24W。我们多次重复这一过程以确认我们的发现,每次断开以太网电缆时,我们总能看到平均功率下降近 1W。因此,我们可以得出结论,使用以太网连接 FPGA 会显著增加功耗。
我们还尝试在 FPGA 中增加两千个 32 位计数器,以观察功耗的变化,但功耗的变化很难观察到。
在这里插入图片描述在这里插入图片描述

图 8:连接以太网电缆后的读数

在这里插入图片描述

图 9:以太网电缆断开时的读数

Code

I2C master code

module i2c_master (

    // basic signal
    input  logic clk,
    input  logic reset, // active high
    // control signals
    input  logic start,
    input  logic stop,
    // i2c ports
    output logic scl,
    inout  wire  sda,
    // To HPS
    output logic [11:0] data_out,
    output logic        data_valid
);

    //-----------------------------------------------------
    //  SIGNAL DEFINITION
    //-----------------------------------------------------
    localparam STATE_IDLE               = 4'd0;
    localparam STATE_START              = 4'd1;
    localparam STATE_ADDR               = 4'd2;
    localparam STATE_SLAVE_ACK          = 4'd3;
    localparam STATE_DATA_RECEIVE_UPPER = 4'd4;
    localparam STATE_MASTER_ACK_UPPER   = 4'd5;
    localparam STATE_DATA_RECEIVE_LOWER = 4'd6;
    localparam STATE_MASTER_ACK_LOWER   = 4'd7;
    localparam STATE_MASTER_NACK        = 4'd8;
    localparam STATE_STOP               = 4'd9;
    localparam STATE_WAIT               = 4'd10;

    logic [2:0] addr_cnt; //  counter for address
    logic [2:0] upper_cnt; // counter for upper 8-bit
    logic [2:0] lower_cnt; // counter for lower 8-bit

    //-----------------------------------------------------
    //  STATE UPDATE
    //-----------------------------------------------------
    logic [3:0] state_current;
    logic [3:0] state_next;

    always @( negedge clk ) begin
        if ( reset ) begin
            state_current <= STATE_IDLE;
        end
        else begin
            state_current <= state_next;
        end
    end

    //----------------------------------------------------------------------
    // STATE TRANSITION
    //----------------------------------------------------------------------
    always_comb begin
        case( state_current )
        
            STATE_IDLE: begin
                if ( start ) state_next = STATE_START;
                else state_next = STATE_IDLE;
            end
            STATE_START: begin
                state_next = STATE_ADDR;
            end
            STATE_ADDR: begin
                if ( addr_cnt == 3'd7 ) state_next = STATE_SLAVE_ACK; 
                else state_next = STATE_ADDR;
            end
            STATE_SLAVE_ACK: begin
                if ( !sda ) state_next = STATE_DATA_RECEIVE_UPPER;
                else state_next = STATE_IDLE;
            end
            STATE_DATA_RECEIVE_UPPER: begin
                if ( upper_cnt == 3'd7 ) state_next = STATE_MASTER_ACK_UPPER;
                else state_next = STATE_DATA_RECEIVE_UPPER;
            end
            STATE_MASTER_ACK_UPPER: begin
                if ( !sda ) state_next = STATE_DATA_RECEIVE_LOWER;
                else state_next = STATE_IDLE;
            end
            STATE_DATA_RECEIVE_LOWER: begin
                if ( lower_cnt == 3'd7 && !stop ) state_next = STATE_MASTER_ACK_LOWER;
                else if ( lower_cnt == 3'd7 && stop ) state_next = STATE_MASTER_NACK;
                else state_next = STATE_DATA_RECEIVE_LOWER;
            end
            STATE_MASTER_ACK_LOWER: begin
                state_next = STATE_DATA_RECEIVE_UPPER;
            end
            STATE_MASTER_NACK: begin
                state_next = STATE_STOP;
            end
            STATE_STOP: begin
                state_next = STATE_WAIT;
            end
            STATE_WAIT: begin
                state_next = STATE_IDLE;
            end

            default:
                state_next = STATE_IDLE;
                
        endcase         
    end

    //----------------------------------------------------------------------
    // STATE OUTPUT
    //----------------------------------------------------------------------
    always_comb begin // sck logic
        if ( ( state_current != STATE_IDLE ) && ( state_current != STATE_START )
         && ( state_current != STATE_STOP ) && ( state_current != STATE_WAIT ) ) scl = clk;
        else if ( state_current == STATE_STOP ) scl = 1'b0;
        else scl = 1'b1;
    end

    logic master_is_input; // tri-state logic for sda
    logic sda_temp;
    assign sda = master_is_input ? 1'bz : sda_temp; 

    always_comb begin
        case( state_current )
            STATE_IDLE: begin
                master_is_input = 1'b0;
            end
            STATE_START: begin
                master_is_input = 1'b0;
            end
            STATE_ADDR: begin
                master_is_input = 1'b0;
            end
            STATE_SLAVE_ACK: begin
                master_is_input = 1'b1;
            end
            STATE_DATA_RECEIVE_UPPER: begin
                master_is_input = 1'b1;
            end
            STATE_MASTER_ACK_UPPER: begin
                master_is_input = 1'b0;
            end
            STATE_DATA_RECEIVE_LOWER: begin
                master_is_input = 1'b1;
            end
            STATE_MASTER_ACK_LOWER: begin
                master_is_input = 1'b0;
            end
            STATE_MASTER_NACK: begin
                master_is_input = 1'b0;
            end 
            STATE_STOP: begin
                master_is_input = 1'b0;
            end
            STATE_WAIT: begin
                master_is_input = 1'b0;
            end
            default: begin
                master_is_input = 1'b1;
            end

        endcase
    end

    always_comb begin // master output for sda logic
        case( state_current )
            STATE_IDLE: begin
                sda_temp = 1'b1;
            end
            STATE_START: begin
                sda_temp = 1'b0;
            end
            STATE_ADDR: begin
                if ( addr_cnt == 0 ) sda_temp = 1'b1;
                else if ( addr_cnt == 1 ) sda_temp = 1'b0;
                else if ( addr_cnt == 2 ) sda_temp = 1'b0;
                else if ( addr_cnt == 3 ) sda_temp = 1'b1;
                else if ( addr_cnt == 4 ) sda_temp = 1'b1;
                else if ( addr_cnt == 5 ) sda_temp = 1'b0;
                else if ( addr_cnt == 6 ) sda_temp = 1'b1;
                else if ( addr_cnt == 7 ) sda_temp = 1'b1;
                else sda_temp = 1'b0;
            end
            STATE_MASTER_ACK_UPPER: begin
                sda_temp = 1'b0;
            end
            STATE_MASTER_ACK_LOWER: begin
                sda_temp = 1'b0;
            end
            STATE_MASTER_NACK: begin
                sda_temp = 1'b1;
            end 
            STATE_STOP: begin
                sda_temp = 1'b0;
            end
            STATE_WAIT: begin
                sda_temp = 1'b0;
            end
            default: begin
                sda_temp = 1'b0;
            end
        endcase
    end

    always_ff @( negedge clk ) begin // data_out store
        if ( ( state_current == STATE_DATA_RECEIVE_UPPER ) && ( upper_cnt <= 7 ) && ( upper_cnt >= 4 ) ) begin
            data_out <= { data_out[10:0], sda };
        end
        else if ( state_current == STATE_DATA_RECEIVE_LOWER ) begin
            data_out <= { data_out[10:0], sda };
        end
    end 

    assign data_valid = ( state_current == STATE_MASTER_ACK_LOWER ) || ( state_current == STATE_MASTER_NACK ); // data valid signal logic  

    //----------------------------------------------------------------------
    // CONTROL SIGNALS
    //----------------------------------------------------------------------
    
    always_ff @( negedge clk ) begin
        if ( state_current == STATE_ADDR ) addr_cnt <= addr_cnt + 1;
        else addr_cnt <= 3'd0;
    end

    always_ff @( negedge clk ) begin
        if ( state_current == STATE_DATA_RECEIVE_UPPER ) upper_cnt <= upper_cnt + 1;
        else upper_cnt <= 3'd0;
    end
    
    always_ff @( negedge clk ) begin
        if ( state_current == STATE_DATA_RECEIVE_LOWER ) lower_cnt <= lower_cnt + 1;
        else lower_cnt <= 3'd0;
    end

endmodule

Hardware implementation on FPGA


//-----------------------------------------------------
//  Segment decoder
//-----------------------------------------------------
 module segment_decoder (
    input  logic               clk,
    //input  logic               current_val,
    input  logic signed [26:0] current,
    output logic         [6:0] hex0,
    output logic         [6:0] hex1,
    output logic         [6:0] hex2, // fraction
    output logic         [6:0] hex3, // integer
    output logic         [6:0] hex4  // sign
);

always_ff@ ( posedge clk ) begin
    if ( current[26] ) hex4 <= 7'b0111111;
    else hex4 <= 7'b1111111;
end

logic signed [4:0] current_int;
assign current_int = current[26:22];

always_ff@ ( posedge clk ) begin
    if ( !current[26] ) begin
        case( current_int )
            5'd1: begin
                hex3 <= 7'b1111001;
            end
            5'd2: begin
                hex3 <= 7'b0100100;
            end
            default: begin
                hex3 <= 7'b1000000;
            end
        endcase
    end
    else begin
        case( current_int )
            -5'd1: begin
                if (current[21:18]==4'd0) hex3 <= 7'b1111001;
                else hex3 <= 7'b1000000;
            end
            -5'd2: begin
                if (current[21:18]==4'd0) hex3 <= 7'b0100100;
                else hex3 <= 7'b1111001;
            end
            default: begin
                hex3 <= 7'b1000000;
            end
        endcase
    end
        
    
end

always_ff@ ( posedge clk ) begin
    case( current[21:18] )
        4'd0: begin
            hex2 <= 7'b1000000;
            hex1 <= 7'b1000000;
            hex0 <= 7'b1000000;
        end
        4'd1: begin
            hex2 <= current[26] ? 7'b0010000 : 7'b1000000;
            hex1 <= current[26] ? 7'b0110000 : 7'b0000010;
            hex0 <= current[26] ? 7'b1111000 : 7'b0110000;
        end
        4'd2: begin
            hex2 <= current[26] ? 7'b0000000 : 7'b1111001;
            hex1 <= current[26] ? 7'b1111000 : 7'b0100100;
            hex0 <= current[26] ? 7'b0010010 : 7'b0010010;
        end
        4'd3: begin
            hex2 <= current[26] ? 7'b0000000 : 7'b1111001;
            hex1 <= current[26] ? 7'b1111001 : 7'b0000000;
            hex0 <= current[26] ? 7'b0100100 : 7'b0000000;
        end
        4'd4: begin
            hex2 <= current[26] ? 7'b1111000 : 7'b0100100;
            hex1 <= current[26] ? 7'b0010010 : 7'b0010010; 
            hex0 <= current[26] ? 7'b1000000 : 7'b1000000;
        end
        4'd5: begin
            hex2 <= current[26] ? 7'b0000010 : 7'b0110000;
            hex1 <= current[26] ? 7'b0000000 : 7'b1111001;
            hex0 <= current[26] ? 7'b1111000 : 7'b0110000;
        end
        4'd6: begin
            hex2 <= current[26] ? 7'b0000010 : 7'b0110000;
            hex1 <= current[26] ? 7'b0100100 : 7'b1111000;
            hex0 <= current[26] ? 7'b0010010 : 7'b0010010;
        end
        4'd7: begin
            hex2 <= current[26] ? 7'b0010010 : 7'b0011001;
            hex1 <= current[26] ? 7'b0000010 : 7'b0110000; 
            hex0 <= current[26] ? 7'b0100100 : 7'b0000000;
        end
        4'd8: begin
            hex2 <= current[26] ? 7'b0010010 : 7'b0010010;
            hex1 <= current[26] ? 7'b1000000 : 7'b1000000;
            hex0 <= current[26] ? 7'b1000000 : 7'b1000000;
        end
        4'd9: begin
            hex2 <= current[26] ? 7'b0011001 : 7'b0010010;
            hex1 <= current[26] ? 7'b0110000 : 7'b0000010;
            hex0 <= current[26] ? 7'b1111000 : 7'b0110000;
        end
        4'd10: begin
            hex2 <= current[26] ? 7'b0110000 : 7'b0000010;
            hex1 <= current[26] ? 7'b1111000 : 7'b0100100;
            hex0 <= current[26] ? 7'b0010010 : 7'b0010010;
        end
        4'd11: begin
            hex2 <= current[26] ? 7'b0110000 : 7'b0000010;
            hex1 <= current[26] ? 7'b1111001 : 7'b0000000;
            hex0 <= current[26] ? 7'b0100100 : 7'b0000000;
        end
        4'd12: begin
            hex2 <= current[26] ? 7'b0100100 : 7'b1111000;
            hex1 <= current[26] ? 7'b0010010 : 7'b0010010;
            hex0 <= current[26] ? 7'b1000000 : 7'b1000000;
        end
        4'd13: begin
            hex2 <= current[26] ? 7'b1111001 : 7'b0000000;
            hex1 <= current[26] ? 7'b0000000 : 7'b1111001;
            hex0 <= current[26] ? 7'b1111000 : 7'b0110000;
        end
        4'd14: begin
            hex2 <= current[26] ? 7'b1111001 : 7'b0000000;
            hex1 <= current[26] ? 7'b0100100 : 7'b1111000;
            hex0 <= current[26] ? 7'b0010010 : 7'b0010010;
        end
        4'd15: begin
            hex2 <= current[26] ?  7'b1000000 : 7'b0010000;
            hex1 <= current[26] ?  7'b0000010 : 7'b0110000;
            hex0 <= current[26] ?  7'b0100100 : 7'b0000000;
        end
    endcase
end
    
endmodule

//-----------------------------------------------------
//  Fixed point multipliers
//-----------------------------------------------------
module signed_mult_27bit
(
    input  logic signed [26:0] a,
    input  logic signed [26:0] b,
    output logic signed [26:0] out
);

logic signed [53:0] mult_out;
assign mult_out = a * b;
assign out = {mult_out[53], mult_out[47:22]};

endmodule

module signed_mult_32bit
(
    input  logic signed [31:0] a,
    input  logic signed [31:0] b,
    output logic signed [31:0] out
);

logic signed [63:0] mult_out;
assign mult_out = a * b;
assign out = {mult_out[63], mult_out[53:23]};

endmodule

//-----------------------------------------------------
//  200kHz clock
//-----------------------------------------------------
module clk_divider_400k(
	input  logic reset,
	input  logic clk_50M,
	output logic clk_400k
);
	logic [6:0]	counter;

	always_ff@( posedge clk_50M ) begin	
		if ( reset ) begin
			clk_400k <= 1'b1;
			counter <= 0;
		end
		else if ( counter == 124 ) begin
			clk_400k <= !clk_400k;
			counter <= 0;
		end
		else begin
			counter <= counter + 1;
		end
	end
endmodule

//-----------------------------------------------------
//  Integrater
//-----------------------------------------------------
module integrater(
	// basic
    input logic         reset,
    input logic         clk,
    // control: signals to start and clear integration
    input logic         data_valid,  // = data_valid from i2c interface
	input logic         start,
	input logic         stop,
	input logic         cali,
    // From I2C interface
    input logic signed [26:0] input_data, // 5.22
    // To HPS 
    output logic signed [63:0] energy_out,
	output logic        [31:0] cycle_cnt
);

    // PARAMETER DEFINITION 
    logic signed [31:0] unit_voltage = 32'd9059; // (2^23)*18*12*1/200000

	logic signed [31:0] input_data_32bit; // 9.23
	assign input_data_32bit = { {4{input_data[26]}}, input_data, 1'b0};

	logic signed [31:0]	unit_energy;

	// UNIT ENERGY
	signed_mult_32bit multiply(
		.a(unit_voltage),
		.b(input_data_32bit),
		.out(unit_energy)
	);

	// CONTROL
	logic out_ind;
	always_ff @( posedge clk or posedge reset or posedge cali ) begin
		if ( reset || cali ) out_ind <= 1'b0;
		else if ( stop ) out_ind <= 1'b1;
		else if ( start ) out_ind <= 1'b0;
	end

	// ENERGY COMPUTATION
	always_ff @( posedge clk or posedge reset or posedge cali ) begin
		if ( reset || cali ) energy_out <= 32'd0;
		else if ( !out_ind && data_valid ) energy_out <= energy_out + { {32{unit_energy[31]}}, unit_energy}; 
	end

	// TIME COUNTER
	always_ff @( posedge clk or posedge reset or posedge cali ) begin
		if ( reset || cali ) cycle_cnt <= 32'd0;
		else if ( !out_ind && data_valid ) cycle_cnt <= cycle_cnt + 1;
	end

endmodule

C code

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <sys/mman.h>
#include <sys/time.h> 
#include <math.h>
#include <string.h>
#include <errno.h>    // Definition for "error handling"
#include <sys/wait.h> // Definition for wait()
//#include "address_map_arm_brl4.h"

// threads
#include <pthread.h>

/****************************************************************************************
 *                              Prameter Definition
****************************************************************************************/

//============= VGA PLOT PARAMS ========================
/* Cyclone V FPGA devices */
#define HW_REGS_BASE          0xff200000
//#define HW_REGS_SPAN        0x00200000 
#define HW_REGS_SPAN          0x00005000 

#define FPGA_ONCHIP_BASE      0xC8000000
//#define FPGA_ONCHIP_END       0xC803FFFF
// modified for 640x480
// #define FPGA_ONCHIP_SPAN      0x00040000
#define FPGA_ONCHIP_SPAN      0x00080000

#define FPGA_CHAR_BASE        0xC9000000 
#define FPGA_CHAR_END         0xC9001FFF
#define FPGA_CHAR_SPAN        0x00002000

//============= ADDRESS OFFSET =============
#define FPGA_CURRENT       	0x00000070
// #define FPGA_ENERGY        	0x00000080
#define FPGA_CYCLE_CNT     	0x00000090
#define FPGA_START         	0x000000a0
#define FPGA_STOP          	0x000000b0
#define FPGA_RESET	       	0x000000c0
#define FPGA_DATA_VALID    	0x000000d0
#define FPGA_CALIBRATE    	0x000000f0
#define ENERGY_OUT_LOWER    0x00000200
#define ENERGY_OUT_UPPER    0x00000100
//======================================================
/* function prototypes */
void VGA_text (int, int, char *);
void VGA_text_clear();
void VGA_box (int, int, int, int, short);
void VGA_line(int, int, int, int, short) ;
void VGA_disc (int, int, int, short);

// 16-bit primary colors
#define red  (0+(0<<5)+(31<<11))
#define dark_red (0+(0<<5)+(15<<11))
#define green (0+(63<<5)+(0<<11))
#define dark_green (0+(31<<5)+(0<<11))
#define blue (31+(0<<5)+(0<<11))
#define dark_blue (15+(0<<5)+(0<<11))
#define yellow (0+(63<<5)+(31<<11))
#define cyan (31+(63<<5)+(0<<11))
#define magenta (31+(0<<5)+(31<<11))
#define black (0x0000)
#define gray (15+(31<<5)+(51<<11))
#define white (0xffff)
int colors[] = {red, dark_red, green, dark_green, blue, dark_blue, 
		yellow, cyan, magenta, gray, black, white};


// 8-bit color
#define rgb(r,g,b) ((((r)&7)<<5) | (((g)&7)<<2) | (((b)&3)))

// pixel macro
#define VGA_PIXEL(x,y,color) do{\
	char  *pixel_ptr ;\
	pixel_ptr = (char *)vga_pixel_ptr + ((y)<<10) + (x) ;\
	*(char *)pixel_ptr = (color);\
} while(0)


// ============== SOLVER-LW-AXI POINTER ================
volatile          char *  fpga_reset_ptr      		= NULL ;
volatile          char *  fpga_start_ptr      		= NULL ;
volatile          char *  fpga_stop_ptr       		= NULL ;
volatile          char *  fpga_data_valid_ptr 		= NULL ;
volatile          signed int *   fpga_current_ptr   = NULL ;
// volatile          signed int *   fpga_energy_ptr    = NULL ;
volatile          int *   fpga_cycle_cnt_ptr  		= NULL ;
volatile          char *  fpga_calibration_ptr      = NULL ;
volatile          unsigned int *  energy_out_lower_ptr 		= NULL ;
volatile          signed int * energy_out_upper_ptr = NULL ;
//======================================================

// the light weight buss base
void *h2p_lw_virtual_base;

// pixel buffer
volatile unsigned int * vga_pixel_ptr = NULL ;
void *vga_pixel_virtual_base;

// character buffer
volatile unsigned int * vga_char_ptr = NULL ;
void *vga_char_virtual_base;

// /dev/mem file id
int fd;

// measure time
struct timeval t1, t2;
double elapsedTime;
/****************************************************************************************
// 							Energy estimator variables
****************************************************************************************/
double time_spent;
float current_float;
double energy_double;
float power_float;
float cycle_cnt;

long long energy_64bit;

// Coordinate variables for plotting graphs
int coord_x_pre;
int coord_x;
signed int current_y_pre;
signed int current_y;
signed int power_y_pre;
signed int power_y;
signed int avg_power_y_pre;
signed int avg_power_y;
double time_start;
double time_pre;

// Time axis variable
int time_axis1;
int time_axis2;
int time_axis3;

// Calibrate indicator
int cali_flag;



/****************************************************************************************
 *                              Thread stuff
****************************************************************************************/

#define TRUE 1
#define FALSE 0



// access to enter condition
// -- for signalling enter done
pthread_mutex_t enter_lock= PTHREAD_MUTEX_INITIALIZER;
// access to print condition
// -- for signalling print done
pthread_mutex_t print_lock= PTHREAD_MUTEX_INITIALIZER;
// counter protection
pthread_mutex_t count_lock= PTHREAD_MUTEX_INITIALIZER;

// the two condition variables related to the mutex above
pthread_cond_t enter_cond ;
pthread_cond_t print_cond ;
pthread_cond_t hardware_cond ;

// globals for perfromance
int count1, count2;

// Thread variables
char input_buffer[64];
// int threshold;

//Thread functions
void * hardware();
void * software();
void * read1();
void * write1();
void * counter1();



	
/****************************************************************************************
 *                              Main Function
****************************************************************************************/
int main()
{

	// === need to mmap: =======================
	// FPGA_CHAR_BASE
	// FPGA_ONCHIP_BASE      
	// HW_REGS_BASE        
  
	// === get FPGA addresses ==================
    // Open /dev/mem
	if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) 	{
		printf( "ERROR: could not open \"/dev/mem\"...\n" );
		return( 1 );
	}
    
    // get virtual addr that maps to physical
	h2p_lw_virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE );	
	if( h2p_lw_virtual_base == MAP_FAILED ) {
		printf( "ERROR: mmap1() failed...\n" );
		close( fd );
		return(1);
	}
    

	// === get VGA char addr =====================
	// get virtual addr that maps to physical
	vga_char_virtual_base = mmap( NULL, FPGA_CHAR_SPAN, ( 	PROT_READ | PROT_WRITE ), MAP_SHARED, fd, FPGA_CHAR_BASE );	
	if( vga_char_virtual_base == MAP_FAILED ) {
		printf( "ERROR: mmap2() failed...\n" );
		close( fd );
		return(1);
	}
    
    // Get the address that maps to the FPGA LED control 
	vga_char_ptr =(unsigned int *)(vga_char_virtual_base);

	// === get VGA pixel addr ====================
	// get virtual addr that maps to physical
	vga_pixel_virtual_base = mmap( NULL, FPGA_ONCHIP_SPAN, ( 	PROT_READ | PROT_WRITE ), MAP_SHARED, fd, 			FPGA_ONCHIP_BASE);	
	if( vga_pixel_virtual_base == MAP_FAILED ) {
		printf( "ERROR: mmap3() failed...\n" );
		close( fd );
		return(1);
	}
    
    // Get the address that maps to the FPGA pixel buffer
	vga_pixel_ptr =(unsigned int *)(vga_pixel_virtual_base);


/****************************************************************************************
 *                              Draw things on VGA
****************************************************************************************/

	/* create a message to be displayed on the VGA 
          and LCD displays */
	char time_text[30] = "Time/s\0";
	char curent_text[30] = "Current/A\0";
	char dyn_power_text[30] = "Dynamic power/W\0";
	char avg_power_text[30] = "Average power/W\0";
	char curren_y_text1[10] = "0.5";
	char curren_y_text2[10] = "1.0";
	char curren_y_text3[10] = "1.5";
	char curren_y_text4[10] = "2.0";
	char dyn_power_y_text1[10] = " 6";
	char dyn_power_y_text2[10] = "12";
	char dyn_power_y_text3[10] = "18";
	char dyn_power_y_text4[10] = "24";
	char avg_power_y_text1[10] = " 6";
	char avg_power_y_text2[10] = "12";
	char avg_power_y_text3[10] = "18";
	char avg_power_y_text4[10] = "24";

	// clear the screen
	VGA_box (0, 0, 639, 479, 0x0000);
	// clear the text
	VGA_text_clear();


	// ====================== Plot graphs ==============================
	// =============== Current ================
	VGA_line(40,20,40,139,dark_blue);
	VGA_line(40,139,420,139,dark_blue);
	VGA_text(4,1,curent_text);
	VGA_text(45,18,time_text);
	// Y axis data points
	// 0.5A
	VGA_line(40,110,37,110,dark_blue);
	VGA_text(1,13,curren_y_text1);
	// 1.0A
	VGA_line(40,81,37,81,dark_blue);
	VGA_text(1,10,curren_y_text2);
	// 1.5A
	VGA_line(40,52,37,52,dark_blue);
	VGA_text(1,6,curren_y_text3);
	// 2.0A
	VGA_line(40,23,37,23,dark_blue);
	VGA_text(1,3,curren_y_text4);

	// ============== Dynamic ower ==============
	VGA_line(40,169,40,288,yellow);
	VGA_line(40,288,420,288,yellow);
	VGA_text(4,19,dyn_power_text);
	VGA_text(45,37,time_text);	
	// Y axis data points
	// 6W
	VGA_line(40,259,37,259,yellow);
	VGA_text(2,33,dyn_power_y_text1);
	// 12kW
	VGA_line(40,230,37,230,yellow);
	VGA_text(2,29,dyn_power_y_text2);
	// 18W
	VGA_line(40,201,37,201,yellow);
	VGA_text(2,25,dyn_power_y_text3);
	// 24W
	VGA_line(40,172,37,172,yellow);
	VGA_text(2,21,dyn_power_y_text4);

	// ================ Average power ==============
	VGA_line(40,329,40,448, magenta);
	VGA_line(40,448,420,448,magenta);
	VGA_text(4,39,avg_power_text);
	// VGA_text(45,57,time_text);	
	// Y axis data points
	// 6W
	VGA_line(40,419,37,419,magenta);
	VGA_text(2,52,avg_power_y_text1);
	// 12kW
	VGA_line(40,390,37,390,magenta);
	VGA_text(2,49,avg_power_y_text2);
	// 18W
	VGA_line(40,361,37,361,magenta);
	VGA_text(2,45,avg_power_y_text3);
	// 24W
	VGA_line(40,332,37,332,magenta);
	VGA_text(2,41,avg_power_y_text4);

	// Time axis
	VGA_line(166,448,166,451,magenta);
	// VGA_text(20,57,time_axis_text1);

	VGA_line(292,448,292,451,magenta);
	// VGA_text(36,57,time_axis_text2);

	VGA_line(418,448,418,451,magenta);
	// VGA_text(51,57,time_axis_text3);

	// =================== Box to print data ===========================
	VGA_line(440,30,630,30,white);
	VGA_line(440,450,630,450,white);
	VGA_line(440,30,440,450,white);
	VGA_line(630,30,630,450,white);


	// =================== LW AXI connection ================================
	fpga_reset_ptr = (char *)(h2p_lw_virtual_base + FPGA_RESET);    
    fpga_start_ptr = (char *)(h2p_lw_virtual_base + FPGA_START);        
	fpga_stop_ptr  = (char *)(h2p_lw_virtual_base + FPGA_STOP);         
	fpga_data_valid_ptr = (char *)(h2p_lw_virtual_base + FPGA_DATA_VALID);    
	fpga_current_ptr = (signed int *)(h2p_lw_virtual_base + FPGA_CURRENT);       
	// fpga_energy_ptr  = (signed int *)(h2p_lw_virtual_base + FPGA_ENERGY);      
	fpga_cycle_cnt_ptr = (int *)(h2p_lw_virtual_base + FPGA_CYCLE_CNT);    
	fpga_calibration_ptr = (char *)(h2p_lw_virtual_base + FPGA_CALIBRATE);
	energy_out_lower_ptr = (unsigned int *)(h2p_lw_virtual_base + ENERGY_OUT_LOWER);
	energy_out_upper_ptr = (signed int *)(h2p_lw_virtual_base + ENERGY_OUT_UPPER);
    // ======================================================================
	printf("start setting power estimator\n");
	*(fpga_stop_ptr) = 0;
	*(fpga_stop_ptr) = 1;
	usleep(1000);
	*(fpga_stop_ptr) = 0;
	printf("set stop to 1\n");
	usleep(100);
	*(fpga_reset_ptr) = 0;
    *(fpga_reset_ptr) = 1;
	usleep(1000);
	*(fpga_reset_ptr) = 0;
	printf("set reset to 1\n");
	usleep(100);
	*(fpga_start_ptr) = 0;
	*(fpga_start_ptr) = 1;
	usleep(1000);
	*(fpga_start_ptr) = 0;
	printf("set start to 1\n");


	// ================== Thread ============================================
	int status;
	// the thread identifiers
	pthread_t thread_read, thread_write, thread_count1, thread_count2, thread_hardware;
	// pthread_t software_thread;
	
	// the condition variables
	pthread_cond_init (&enter_cond, NULL);
	pthread_cond_init (&print_cond, NULL);
	pthread_cond_init (&hardware_cond, NULL);
	
	//For portability, explicitly create threads in a joinable state 
	// thread attribute used here to allow JOIN
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	
	// now the threads
	pthread_create(&thread_read,NULL,read1,NULL);
	pthread_create(&thread_write,NULL,write1,NULL);
	pthread_create(&thread_count1,NULL,counter1,NULL);
	pthread_create(&thread_hardware, NULL, hardware(),NULL);
	pthread_join(thread_read,NULL);
	pthread_join(thread_write,NULL);

	// Initialize calibration flag
	cali_flag = 0;

} // end main

/****************************************************************************************
 *                          Thread functions
****************************************************************************************/
void * hardware(){
		// print data string
	char current_string[20] = "Current:";
	char current_data_string[20];
	char dyn_power_string[20] = "Dynamic power:";
	char dyn_power_data_string[20];
	char avg_power_string[20] = "Average power:";
	char avg_power_data_string[20];
	char energy_string[20] = "Energy:";
	char energy_data_string[20];
	char time_string[20] = "Time:";
	char time_data_string[20];
	char current_unit_string[5] = "A";
	char power_unit_string[5] = "W";
	char energy_unit_string[5] = "J";
	char time_unit_string[5] = "s";
	char title_string[30] = "==== Data table ====";
	char assume_vol1[20] = "Assuming constant";
	char assume_vol2[20] = "voltage at 12V";
	char choose_operation[30] = "Choose from below:";
	char operation1[30] = "1 -- start";
	char operation2[30] = "2 -- stop";
	char operation3[30] = "3 -- calibrate";

	char time_axis_text1[10];
	char time_axis_text2[10];
	char time_axis_text3[10];
	
	// Initialize variable
	time_start = 0;
	time_axis1 = 1;
	time_axis2 = 2;
	time_axis3 = 3;

	// Dot coordinate
	coord_x_pre = 41;
	current_y_pre = 139;
	power_y_pre = 288;
	avg_power_y_pre = 448;

	time_pre = 0;

    while(1){
		// start timer
		gettimeofday(&t1, NULL);
		

        if(*(fpga_data_valid_ptr) == 1){
			current_float = (float)(*(fpga_current_ptr)/pow(2,23));
			// energy_float = (float)((*(fpga_energy_ptr)/pow(2,23)));
			energy_64bit = (((long long) *(energy_out_upper_ptr))<<32) | *(energy_out_lower_ptr);
			energy_double = energy_64bit/pow(2,23);
			cycle_cnt = *(fpga_cycle_cnt_ptr);
			time_spent = ((cycle_cnt-1)*18)/200000;
			usleep(1000);

			coord_x = 41 + (time_spent - time_start)/0.00789; 

			current_y = 139 - current_float/0.0167;
			power_y = 288 - (current_float*12)/0.2017;
			avg_power_y = 448 - (energy_double/time_spent)/0.2017;

			if(current_y > 139 ){
				current_y = 139;
			}
			if(power_y > 288){
				power_y = 288;
			}
			if(avg_power_y > 448){
				power_y = 448;
			}

			// VGA_PIXEL(coord_x, current_y, dark_blue);
			// VGA_PIXEL(coord_x, power_y, yellow);
			// VGA_PIXEL(coord_x,avg_power_y,magenta);

			if(cali_flag){
				// clear graphs
				VGA_box (41, 15, 435, 138, 0x000);
				VGA_box (41, 164, 435, 287, 0x000);
				VGA_box (41, 324, 435, 447, 0x000);
				time_axis1 = 1;
				time_axis2 = 2;
				time_axis3 = 3;
				coord_x_pre = 41;
				coord_x = 41;
				time_pre = 0;
				cali_flag = 0;
				time_start = 0;
			}

			VGA_line(coord_x_pre, current_y_pre,coord_x, current_y,dark_blue);
			VGA_line(coord_x_pre, power_y_pre, coord_x, power_y, yellow);
			VGA_line(coord_x_pre, avg_power_y, coord_x, avg_power_y, magenta);

			if(time_spent-time_pre>3){
				// clear graphs
				VGA_box (41, 15, 435, 138, 0x000);
				VGA_box (41, 164, 435, 287, 0x000);
				VGA_box (41, 324, 435, 447, 0x000);
				time_start = time_spent;
				// increment time
				time_axis1 = time_axis3 + 1;
				time_axis2 = time_axis3 + 2;
				time_axis3 = time_axis3 + 3;
				coord_x_pre = 41;
				coord_x = 41;
				time_pre = time_pre + 3;
			}

			// // Print current and power data on VGA
			VGA_text (57, 7, title_string);
			VGA_text(57,9,assume_vol1);
			VGA_text(57,10,assume_vol2);
			// Current
			VGA_text (57, 16, current_string);
			sprintf(current_data_string, "%f", current_float);
			VGA_text (57, 17, current_data_string);
			VGA_text (68, 17, current_unit_string);

			// Dynamic power
			VGA_text (57, 19, dyn_power_string);
			sprintf(dyn_power_data_string, "%f", current_float*12);
			VGA_text (57, 20, dyn_power_data_string);
			VGA_text (68, 20, power_unit_string);
			// Average power
			VGA_text (57, 22, avg_power_string);
			sprintf(avg_power_data_string, "%lf", energy_double/time_spent);
			VGA_text (57, 23, avg_power_data_string);
			VGA_text (68, 23, power_unit_string);
			// Energy
			VGA_text (57, 25, energy_string);
			sprintf(energy_data_string, "%lf", energy_double);
			VGA_text (57, 26, energy_data_string);
			VGA_text (68, 26, energy_unit_string);
			// Time
			VGA_text (57, 28, time_string);
			sprintf(time_data_string, "%lf", time_spent);
			VGA_text (57, 29, time_data_string);
			VGA_text (57, 29, time_data_string);
			VGA_text (68, 29, time_unit_string);

			// Choose from operations text
			VGA_text(57, 34, choose_operation);
			VGA_text(57, 38, operation1);
			VGA_text(57, 40, operation2);
			VGA_text(57, 42, operation3);
			
			// Time axis
			sprintf(time_axis_text1, "%d   ", time_axis1);
			VGA_text (20, 57, time_axis_text1);
			sprintf(time_axis_text2, "%d   ", time_axis2);
			VGA_text(36,57,time_axis_text2);
			sprintf(time_axis_text3, "%d   ", time_axis3);
			VGA_text(51,57,time_axis_text3);

			coord_x_pre = coord_x;
			current_y_pre = current_y;
			power_y_pre = power_y;
			avg_power_y_pre = avg_power_y;

        }
	}
}

void * read1()
{
		printf("\n");
		printf("Power estimator user interface\n");
		printf("**************************************************************\n");
		while(1){
                //wait for print done
				pthread_mutex_lock(&print_lock);
				pthread_cond_wait(&print_cond,&print_lock);
				// the actual enter				
                printf("Plese choose an operation:\n");
				printf("1-start   2-stop   3-calibrate\n");
                scanf("%s",input_buffer);
				// unlock the input_buffer
                pthread_mutex_unlock(&print_lock);
			    // and tell write1 thread that enter is complete
                pthread_cond_signal(&enter_cond);
        } // while(1)
}

void * write1() {
		sleep(1);
		// signal that the print process is ready when started
		pthread_cond_signal(&print_cond);
		
        while(1){
				// wait for enter done
                pthread_mutex_lock(&enter_lock);
                pthread_cond_wait(&enter_cond,&enter_lock);
				// the protected print (with protected counter)
				pthread_mutex_lock(&count_lock);
				gettimeofday(&t1, NULL);

				if(strcmp(input_buffer,"1")==0){
					*(fpga_start_ptr) = 0;
    				*(fpga_start_ptr) = 1;
					usleep(1000);
					*(fpga_start_ptr) = 0;
				}
				else if(strcmp(input_buffer,"2")==0){
					*(fpga_stop_ptr) = 0;
    				*(fpga_stop_ptr) = 1;
					usleep(1000);
					*(fpga_stop_ptr) = 0;
				}
				else if(strcmp(input_buffer,"3")==0){
					*(fpga_calibration_ptr) = 0;
    				*(fpga_calibration_ptr) = 1;
					usleep(1000);
					*(fpga_calibration_ptr) = 0;
					cali_flag = 1;
				}
				count1 = 0;
				pthread_mutex_unlock(&count_lock);
				// unlock the input_buffer
				pthread_mutex_unlock(&enter_lock);
                // and tell read1 thread that print is done
				pthread_cond_signal(&print_cond);         
        } // while(1)
}

void * counter1() {
		//
        while(1){
				// count as fast as possible
				pthread_mutex_lock(&count_lock);
                count1++;    
				pthread_mutex_unlock(&count_lock);
				              
        } // while(1)
}

/****************************************************************************************
 * Subroutine to send a string of text to the VGA monitor 
****************************************************************************************/
void VGA_text(int x, int y, char * text_ptr)
{
  	volatile char * character_buffer = (char *) vga_char_ptr ;	// VGA character buffer
	int offset;
	/* assume that the text string fits on one line */
	offset = (y << 7) + x;
	while ( *(text_ptr) )
	{
		// write to the character buffer
		*(character_buffer + offset) = *(text_ptr);	
		++text_ptr;
		++offset;
		// Added to slow print on vga
		usleep(250);
	}
}

/****************************************************************************************
 * Subroutine to clear text to the VGA monitor 
****************************************************************************************/
void VGA_text_clear()
{
  	volatile char * character_buffer = (char *) vga_char_ptr ;	// VGA character buffer
	int offset, x, y;
	for (x=0; x<79; x++){
		for (y=0; y<59; y++){
	/* assume that the text string fits on one line */
			offset = (y << 7) + x;
			// write to the character buffer
			*(character_buffer + offset) = ' ';		
		}
	}
}

/****************************************************************************************
 * Draw a filled rectangle on the VGA monitor 
****************************************************************************************/
#define SWAP(X,Y) do{int temp=X; X=Y; Y=temp;}while(0) 

void VGA_box(int x1, int y1, int x2, int y2, short pixel_color)
{
	char  *pixel_ptr ; 
	int row, col;

	/* check and fix box coordinates to be valid */
	if (x1>639) x1 = 639;
	if (y1>479) y1 = 479;
	if (x2>639) x2 = 639;
	if (y2>479) y2 = 479;
	if (x1<0) x1 = 0;
	if (y1<0) y1 = 0;
	if (x2<0) x2 = 0;
	if (y2<0) y2 = 0;
	if (x1>x2) SWAP(x1,x2);
	if (y1>y2) SWAP(y1,y2);
	for (row = y1; row <= y2; row++)
		for (col = x1; col <= x2; ++col)
		{
			//640x480
			pixel_ptr = (char *)vga_pixel_ptr + (row<<10)    + col ;
			// set pixel color
			*(char *)pixel_ptr = pixel_color;		
		}
}

/****************************************************************************************
 * Draw a filled circle on the VGA monitor 
****************************************************************************************/

void VGA_disc(int x, int y, int r, short pixel_color)
{
	char  *pixel_ptr ; 
	int row, col, rsqr, xc, yc;
	
	rsqr = r*r;
	
	for (yc = -r; yc <= r; yc++)
		for (xc = -r; xc <= r; xc++)
		{
			col = xc;
			row = yc;
			// add the r to make the edge smoother
			if(col*col+row*row <= rsqr+r){
				col += x; // add the center point
				row += y; // add the center point
				//check for valid 640x480
				if (col>639) col = 639;
				if (row>479) row = 479;
				if (col<0) col = 0;
				if (row<0) row = 0;
				pixel_ptr = (char *)vga_pixel_ptr + (row<<10) + col ;
				// set pixel color
				*(char *)pixel_ptr = pixel_color;
			}
					
		}
}

// =============================================
// === Draw a line
// =============================================
//plot a line 
//at x1,y1 to x2,y2 with color 
//Code is from David Rodgers,
//"Procedural Elements of Computer Graphics",1985
void VGA_line(int x1, int y1, int x2, int y2, short c) {
	int e;
	signed int dx,dy,j, temp;
	signed int s1,s2, xchange;
     signed int x,y;
	char *pixel_ptr ;
	
	/* check and fix line coordinates to be valid */
	if (x1>639) x1 = 639;
	if (y1>479) y1 = 479;
	if (x2>639) x2 = 639;
	if (y2>479) y2 = 479;
	if (x1<0) x1 = 0;
	if (y1<0) y1 = 0;
	if (x2<0) x2 = 0;
	if (y2<0) y2 = 0;
        
	x = x1;
	y = y1;
	
	//take absolute value
	if (x2 < x1) {
		dx = x1 - x2;
		s1 = -1;
	}

	else if (x2 == x1) {
		dx = 0;
		s1 = 0;
	}

	else {
		dx = x2 - x1;
		s1 = 1;
	}

	if (y2 < y1) {
		dy = y1 - y2;
		s2 = -1;
	}

	else if (y2 == y1) {
		dy = 0;
		s2 = 0;
	}

	else {
		dy = y2 - y1;
		s2 = 1;
	}

	xchange = 0;   

	if (dy>dx) {
		temp = dx;
		dx = dy;
		dy = temp;
		xchange = 1;
	} 

	e = ((int)dy<<1) - dx;  
	 
	for (j=0; j<=dx; j++) {
		//video_pt(x,y,c); //640x480
		pixel_ptr = (char *)vga_pixel_ptr + (y<<10)+ x; 
		// set pixel color
		*(char *)pixel_ptr = c;	
		 
		if (e>=0) {
			if (xchange==1) x = x + s1;
			else y = y + s2;
			e = e - ((int)dx<<1);
		}

		if (xchange==1) y = y + s2;
		else x = x + s1;

		e = e + ((int)dy<<1);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/541828.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Godot4.2】CanvasItem绘图函数全解析 - 8.绘制点索引

概述 在示意图绘制过程中或者测试过程中&#xff0c;可能需要标记点的索引。 最常见的形式就是用一个圆圈作为背景&#xff0c;用阿拉伯数字作为索引。 实现的重点是动态计算背景圆的半径。原理是&#xff0c;获取字符串的矩形&#xff0c;取对角线长度的一半作为外接圆的半…

【JavaScript | RegExp】正则表达式

本文原创于CSDN秋说&#xff0c;未经授权&#xff0c;不得转载。 文章目录 定义特殊字符元字符字符表格汇总特殊结构标记优先级实例匹配常见的 HTTP 或 HTTPS URL匹配路径匹配URL的各个部分全局搜索邮箱正则表达式日期匹配正则表达式特殊字段匹配正则表达式匹配邮箱正则表达式用…

在Windows中用命令行编译C项目

在Windows中可以用命令行编译C项目 官方指导文档&#xff1a; 演练&#xff1a;在命令行上编译 C 程序 | Microsoft Learn 在官方文档中可以看到&#xff0c;可以只安装VS的命令行工具集&#xff0c;如下图所示

MySQL8.0.36-社区版:错误日志(2)

mysql有个错误日志&#xff0c;是专门记录错误信息的&#xff0c;这个功能默认是开启的 一般都是在/var/log/mysqld.log 日志中存放 1.错误日志的位置 首先我们使用命令去查看一下&#xff0c;这个错误日志文件究竟在哪 进入到mysql中&#xff0c;使用命令 show variables…

009、Python+fastapi,第一个后台管理项目走向第9步:ubutun 20.04下安装vscode+git环境配置

一、说明 git是一定要配置的&#xff0c;由于是白嫖的无影云电脑&#xff0c;东西得保存在网上&#xff0c;就继续白嫖gitee吧&#xff0c;显然国内github是不太合适的了 二、安装git 直接安装sudo apt install -y git git --version git version 2.25.1 三、配置git 在git上…

使用阿里云试用Elasticsearch学习:使用内置模型 lang_ident_model_1 创建管道并使用

文档&#xff1a;https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-deploy-model.html 部署刚刚下载好的内置模型 部署内存不够用 还得花钱&#xff0c;拉几把倒吧。就用自带的吧。 测试模型 POST _ml/trained_models/lang_ident_model_1/_infer {"doc…

MySQL-触发器:触发器概述、触发器的创建、查看删除触发器、 触发器的优缺点

触发器 触发器1. 触发器概述2. 触发器的创建2.1 创建触发器语法2.2 代码举例 3. 查看、删除触发器3.1 查看触发器3.2 删除触发器 4. 触发器的优缺点4.1 优点4.2 缺点4.3 注意点 注&#xff1a;此为笔者学习尚硅谷-宋红康MySQL的笔记&#xff0c;其中包含个人的笔记和理解&#…

HarmonyOS实战开发-横竖屏切换

介绍 本实例展示如何使用媒体查询&#xff0c;通过ohos.mediaquery 接口完成在不同设备上显示不同的界面效果。 效果预览 使用说明 1.在竖屏设备上&#xff0c;首页展示新闻列表&#xff0c;点击新闻进入详情界面。 2.在横屏设备上&#xff0c;首页左侧展示新闻列表&#x…

RT-Thread内核简介

1、RT-Thread 内核介绍 RT-Thread 内核架构图,内核处于硬件层之上,内 核部分包括内核库、实时内核实现 内核库是为了保证内核能够独立运行的一套小型的类似 C 库的函数实现子集。这部分根据编译器的不 同自带 C 库的情况也会有些不同,当使用 GNU GCC 编译器时,会携带…

STM32外设配置以及一些小bug总结

USART RX的DMA配置 这里以UART串口1为例&#xff0c;首先点ADD添加RX和TX配置DMA&#xff0c;然后模式一般会选择是normal&#xff0c;这个模式是当DMA的计数器减到0的时候就不做任何动作了&#xff0c;还有一种循环模式&#xff0c;是计数器减到0之后&#xff0c;计数器自动重…

【GD32】MQ-3酒精检测传感器

2.31 MQ-3酒精检测传感器 MQ-3气体传感器所使用的气敏材料是在清洁空气中电导率较低的二氧化锡(Sn0)。当传感器所处环境中存在酒精蒸气时&#xff0c;传感器的电导率随空气中酒精蒸气浓度的增加而增大。使用简单的电路即可将电导率的变化转换为与该气体浓度相对应的输出信号。…

嵌入式STM32F407CET6移植OpenHarmony系统方法

第一:【实验目的】 1、STM32F407CET6开发版移植鸿蒙系统的方式 第二:【实验原理】 涉及到原理图添加原理图--普通STM32F407原理图第三:【实验步骤】 一、下载LiteOs源码,复制到到虚拟机中并解压 https://gitee.com/LiteOS/LiteOS

政安晨:【Keras机器学习实践要点】(二十九)—— 半监督图像分类使用具有SimCLR对比性预训练的方法

目录 介绍 半监督学习 对比学习 设置 超参数设置 数据集 图像增强 编码器结构 有监督基线模型 用于对比预训练的自我监督模型 对预训练编码器进行有监督微调 与基准线的比较 进一步改进 架构 超参数 相关工作 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x…

Guava里一些比较常用的工具

随着java版本的更新提供了越来越多的语法和工具来简化日常开发&#xff0c;但是我们一般用的比较早的版本所以体验不到。这时就用到了guava这个包。guava提供了很多方便的工具方法&#xff0c;solar框架就依赖了guava的16.0.1版本&#xff0c;这里稍微介绍下。 一、集合工具类…

20240413,类和对象

对象&#xff1a;一切都可为对象&#xff0c;类&#xff1a;相同特性的对象&#xff1b;面向对象特性&#xff1a;封装&#xff0c;继承&#xff0c;多态 一&#xff0c;封装 CLASS 类名 { 访问权限 &#xff1a;属性/行为 } 实例化&#xff1a;通过一个类&#xff0c;创建一…

0101tomcat部署war访问mysql失败-容器间通信-docker项目部署

文章目录 一、简介二、部署1、mysql数据迁移2、docker部署redis3、docker部署tomcat并运行war包 三、报错四、解决1 分析2 解决 结语 一、简介 最近参与开发一个项目&#xff0c;其中一部分系统需要迁移。从阿里云迁移到实体服务器&#xff0c;使用docker部署。系统使用Java语…

11 Php学习:函数

PHP 内建函数Array 函数 PHP Array 函数是 PHP 核心的组成部分。无需安装即可使用这些函数。 创建 PHP 函数 当您需要在 PHP 中封装一段可重复使用的代码块时&#xff0c;可以使用函数。下面详细解释如何创建 PHP 函数并举例说明。 创建 PHP 函数的语法 PHP 函数的基…

TCP-IP详解卷一:协议——阅读总结

该内容适合程序员查看 第1章 概述 1.1 引言 WAN全称是 Wide Area Network&#xff0c;中文名为广域网。 LAN全称是 Local Area Network&#xff0c;中文名为局域网。 1.2分层 ICP/IP协议族通常被认为是一个四层协议系统 分层协议应用层Telnet、FTP和e-mail运输层TCP和UDP网…

shell 调用钉钉通知

使用场景&#xff1a;机器能访问互联网&#xff0c;运行时间任务后通知使用 钉钉建立单人群 手机操作&#xff0c;只能通过手机方式建立单人群 电脑端 2. 配置脚本 #!/bin/bash set -e## 上图中 access_token字段 TOKEN KEYWORDhello # 前文中设置的关键字 function call_…

机器学习——自动驾驶

本章我们主要学习以下内容: 阅读自动驾驶论文采集数据根据论文搭建自动驾驶神经网络训练模型在仿真环境中进行自动驾驶 论文介绍 本文参考自2016年英伟达发表的论文《End to End Learning for Self-Driving Cars》 📎end2end.pdf