Inspider by First hardware project with the Xilinx Kria KV260 kit I've decided to expand this project a little bit and create simple, hardware only temperature control loop that makes the noisy fan of KV260 board load dependend and pretty much inaudible for most of the time making the rest of the development workflow nicer for the ears.
Boardconfig
Nothing special here. Just create new project in Vivado 2021.1
and select KV260 board. Also under board click on "Connections" and select "Vision AI Starter Kit carrier card" to automatically configure Zynq Ultrascale+ with all peripherals from the carrier card.
Now, place the components as described in Step 1 of this official Xilinx guide. Note: 100MHz and 400MHz clocks are not used in this project so I just didn't placed them (Dont't have a lot of experience with FPGAs and these might be used by some auto linker magic? If I'm wrong please correct me).
New IPsNow, double click on AXI Interconnect block and increase number of Master Interfaces to 2 and add "System Management Wizard" IP core to the project. This block communicates with the Zynq Ultrascale+ MPSoC and serves some useful info to our programmable logic, such as system voltage info, alarms, external ADC readings and the most important for us: the SoC's temperature. Configure the block as in the pictures below:
In short, we've dissabled any readings except the temperature that comes from temp_out[9:0] as 10 bit ADC value.
Now, connect the input ports of this block: S_AXI_LITE to AXI Interconnect's M01_AXI,s_axi_aclk to 200MHZ clk_out and s_axi_aresetn to peripheral_aresetn[0:0] of Processor System Reset block that is connected to 200MHz clock. After that, the design should look somehow like this:
In Sources -> Design Sources add three new Verilog design sources named temp2pwm.v, divider.v and pwm.v and copy this lil Verilog code into them:
pwm.vmodule pwm(
input clk,
input [7:0] fill,
input rst,
output reg state
);
integer counter = 'd0;
always @ (posedge clk, negedge rst) begin
// Reset pulled low so keep initial state.
if (rst == 'b0) begin
state <= 'b1;
counter <= 'd0;
end
// Normal PWM operation.
else begin
counter <= (counter + 'd1) % 'd255;
state <= !(counter < fill);
end
end
endmodule
This is a standard 8 bit PWM controller with external reset.
divider.vmodule divider(
input clk_in,
input rst,
output reg clk_out
);
integer counter = 'd0;
always @ (posedge clk_in, negedge rst) begin
// Reset pulled low so keep initial state.
if (rst == 'b0) begin
counter <= 'd0;
clk_out <= 'b0;
end
else begin
counter <= (counter + 'd1) % 'd100000; // 200MHz to 2kHz.
clk_out <= !counter ? ~clk_out : clk_out;
end
end
endmodule
This module divides our 200MHz clock into 2kHz clock usable by PWM module and fan's GPIO (I've tried to use 20kHz but the GPIO behaved chaotically, not sure if the GPIO couldn't handle the speed, there was a problem with PWM module or I didn't configure something with GPIOs, which is the most probable option).
temp2pwm.vmodule temp2pwm(
input rdy,
input [9:0] temp,
output reg [7:0] pwm
);
// C deg * 100.
parameter FAN_0_TEMP = 0000;
parameter FAN_100_TEMP = 6500;
// Linear eq params.
parameter a = 255.0 / ((FAN_100_TEMP - FAN_0_TEMP) / 100);
parameter b = -a * FAN_0_TEMP / 100;
integer temp_deg = 'd0;
integer desired_pwm = 0;
always @ (posedge rdy) begin
// Convert into C degrees * 100 using values from SYSMON4
// transfer function with use of internal reference.
temp_deg = ((temp * 50931 / 1024) - 28023);
desired_pwm = a * (temp_deg / 100) + b; // Compute desired PWM.
desired_pwm = desired_pwm > 255 ? 255 : desired_pwm; // Upper saturation of desired PWM to 255.
pwm = temp_deg <= FAN_0_TEMP ? 'd0 : desired_pwm; // Lower saturation of desired PWM to 0.
end
endmodule
This module converts our 10 bit temperature reading into celsius degrees (based on UltraScale Architecture System Monitor User Guide and then into 8 bit PWM signal. Values taken from the paper were multiplied by 100 and the celsius result divided by 100 to avoid non-supported by Verilog floating point operations whilist keeping somewhat accurate integer temperature reading. Note FAN_0_TEMP and FAN_100_TEMP parameters. These define temperatures required to run the fan on 0% and 100% speed (6500 means 65 C deg), tune them as you please.
Now place the newly created blocks into the block design and connect:
divider.vclk_in to 200MHz clk_out, rst to 200MHz Processor System Reset's peripheral_aresetn[0:0]
temp2pwm.vrdy to System Management's Wizard eos_out and temp[9:0] to same block's temp_out[9:0]
pwm.vclk to divider's clk_out, fill[7:0] to temp2pwm's pwm[7:0] and rst to 200MHz Processor System Reset's peripheral_aresetn[0:0].
After that, right click and Create Port as output named fan_pin and connect it to pwm's state.
The last thing to do is to Open Elaborated Design under Project Manager and set fan_pin's IOSTANDARD as LVCMOS33 and PACKAGE_PIN as A12. After that just generate bitstream and upload it to the board and enjoy the silent treatment of your KV260 board!
Note 1: you have to have SD card with Linux image inserted into the slot, otherwise the 200MHz clock won't work.
Note 2: you probably won't be able to run new accelerated apps while using this hardware bitstream until you recompile PetaLinux with this project's hardware design.
Note 3: With this design, the board keeps steady 30 C degs on idle.
Note 4: I'm not an FPGA developer so tips and tricks are welcomed :)
Note 5: If you're lazy, feel free to use ready to deploy source code listed below.
Comments