APB TIMER验证[二]
APB TIMER 源码分析
// Programmer's model
// 0x00 RW CTRL[3:0]
// [3] Timer Interrupt Enable
// [2] Select External input as Clock
// [1] Select External input as Enable
// [0] Enable
// 0x04 RW Current Value[31:0]
// 0x08 RW Reload Value[31:0]
// 0x0C R/Wc Timer Interrupt
// [0] Interrupt, write 1 to clear
// 0x3E0 - 0x3FC ID registers
module cmsdk_apb_timer(
input wire PCLK, //
input wire PCLKG, //Gated clock
input wire PRESETn, //
input wire PSEL, //device select
input wire[11:2] PADDR, //
input wire PENABLE, //transfer control
input wire PWRITE, //
input wire[31:0] PWDATA, //
input wire[3:0] ECOREVNUM, //
output wire[31:0] PRDATA, //
output wire PREADY, //device ready
output wire PSLVERR, //device error response
input wire EXTIN, //external input
output wire TIMERINT //timer interrupt output
);
//signals for read/write controls
wire read_enable; //读使能
wire write_enable; //写使能
wire write_enable00; //control寄存器写使能
wire write_enable04; //value寄存器写使能
wire write_enable08; //reload寄存器写使能
wire write_enable0c; //中断寄存器写使能
reg [7:0] read_mux_byte0;
reg [7:0] read_mux_byte0_reg;
reg [31:0] read_mux_word;
wire [3:0] pid3_value;
//signals for control registers
reg [3:0] reg_ctrl;
reg [31:0] reg_curr_val;
reg [31:0] reg_reload_val;
reg [31:0] nxt_curr_val;
//Internal signals
reg ext_in_sync1; //外部输入一级同步寄存器
reg ext_in_sync2; //外部输入二级同步寄存器
reg ext_in_delay; //delay register for edge detection
wire ext_in_enable; //外部输入使能控制
wire dec_ctrl; //计数器递减控制
wire clk_ctrl; //clk select result
wire enable_ctrl; //enable select result
wire edge_detect; //边沿检测
reg reg_timer_int; //计数器中断输出寄存器
wire timer_int_clear; //清除timer中断状态
wire timer_int_set; //设置timer中断状态
wire update_timer_int; //update timer interrupt output register
//start of main code
// Read and write control signals
assign read_enable = PSEL & (~PWRITE); // assert for whole APB read transfer
assign write_enable = PSEL & (~PENABLE) & PWRITE; // assert for 1st cycle of write transfer
assign write_enable00 = write_enable & (PADDR[11:2] == 10'h000);//CTRL REGISTER
assign write_enable04 = write_enable & (PADDR[11:2] == 10'h001);//VALUE
assign write_enable08 = write_enable & (PADDR[11:2] == 10'h002);//RELOAD
assign write_enable0c = write_enable & (PADDR[11:2] == 10'h003);//INTSTATUS INTCLEAR
// Write operations
// Control register
always @(posedge PCLKG or negedge PRESETn)
begin
if (~PRESETn)
reg_ctrl <= {4{1'b0}};
else if (write_enable00)
reg_ctrl <= PWDATA[3:0];
end
// Current Value register
always @(posedge PCLK or negedge PRESETn)
begin
if (~PRESETn)
reg_curr_val <= {32{1'b0}};
else if (write_enable04 | dec_ctrl)
reg_curr_val <= nxt_curr_val;
end
// Reload Value register
always @(posedge PCLKG or negedge PRESETn)
begin
if (~PRESETn)
reg_reload_val <= {32{1'b0}};
else if (write_enable08)
reg_reload_val <= PWDATA[31:0];
end
//read operation,partitioned into two parts to reduce gate counts and improve timing
assign pid3_value = ARM_CMSDK_APB_TIMER_PID3;
//lower 8 bits-registered.current value register mux not done here because the value
//can change every cycle
//读拼接的0字节[低8位]
always @(PADDR or reg_ctrl or reg_reload_val or reg_timer_int or ECOREVNUM or pid3_value)
begin
if (PADDR[11:4] == 8'h00) begin
case (PADDR[3:2])
2'h0: read_mux_byte0 = {{4{1'b0}}, reg_ctrl};
2'h1: read_mux_byte0 = {8{1'b0}};
2'h2: read_mux_byte0 = reg_reload_val[7:0];
2'h3: read_mux_byte0 = {{7{1'b0}}, reg_timer_int};
default: read_mux_byte0 = {8{1'bx}};// x propogation
endcase
end
else if (PADDR[11:6] == 6'h3F) begin
case (PADDR[5:2])
4'h0, 4'h1,4'h2,4'h3: read_mux_byte0 = {8{1'b0}};
// ID register - constant values
4'h4: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID4; // 0xFD0 : PID 4
4'h5: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID5; // 0xFD4 : PID 5
4'h6: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID6; // 0xFD8 : PID 6
4'h7: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID7; // 0xFDC : PID 7
4'h8: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID0; // 0xFE0 : PID 0 APB timer part number[7:0]
4'h9: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID1; // 0xFE0 : PID 1 [7:4] jep106_id_3_0. [3:0] part number [11:8]
4'hA: read_mux_byte0 = ARM_CMSDK_APB_TIMER_PID2; // 0xFE0 : PID 2 [7:4] revision, [3] jedec_used. [2:0] jep106_id_6_4
4'hB: read_mux_byte0 = {ECOREVNUM[3:0],pid3_value[3:0]};
// 0xFE0 : PID 3 [7:4] ECO revision, [3:0] modification number
4'hC: read_mux_byte0 = ARM_CMSDK_APB_TIMER_CID0; // 0xFF0 : CID 0
4'hD: read_mux_byte0 = ARM_CMSDK_APB_TIMER_CID1; // 0xFF4 : CID 1 PrimeCell class
4'hE: read_mux_byte0 = ARM_CMSDK_APB_TIMER_CID2; // 0xFF8 : CID 2
4'hF: read_mux_byte0 = ARM_CMSDK_APB_TIMER_CID3; // 0xFFC : CID 3
default : read_mux_byte0 = {8{1'bx}}; // x propogation
endcase
end
else begin
read_mux_byte0 = {8{1'b0}}; //default read out value
end
end
// Register read data
always @(posedge PCLKG or negedge PRESETn)
begin
if (~PRESETn)
read_mux_byte0_reg <= {8{1'b0}};
else if (read_enable)
read_mux_byte0_reg <= read_mux_byte0;
end
// Second level of read mux
always @(PADDR or read_mux_byte0_reg or reg_curr_val or reg_reload_val)
begin
if (PADDR[11:4] == 8'h00) begin
case (PADDR[3:2])
2'b01: read_mux_word = {reg_curr_val[31:0]};
2'b10: read_mux_word = {reg_reload_val[31:8],read_mux_byte0_reg};
2'b00,2'b11: read_mux_word = {{24{1'b0}} ,read_mux_byte0_reg};
default : read_mux_word = {32{1'bx}};
endcase
end
else begin
read_mux_word = {{24{1'b0}} ,read_mux_byte0_reg};
end
end
// Output read data to APB
assign PRDATA = (read_enable) ? read_mux_word : {32{1'b0}};
assign PREADY = 1'b1; // Always ready
assign PSLVERR = 1'b0; // Always okay
assign ext_in_enable = reg_ctrl[1] | reg_ctrl[2] | PSEL;
// Synchonize input and delay for edge detection
always @(posedge PCLK or negedge PRESETn)
begin
if (~PRESETn)
begin
ext_in_sync1 <= 1'b0;
ext_in_sync2 <= 1'b0;
ext_in_delay <= 1'b0;
end
else if (ext_in_enable)
begin
ext_in_sync1 <= EXTIN;
ext_in_sync2 <= ext_in_sync1;
ext_in_delay <= ext_in_sync2;
end
end
// Edge detection,检测EXTIN的低到高翻转
assign edge_detect = ext_in_sync2 & (~ext_in_delay);
// Clock selection
// 如果ctrl[2]=1, 则EXTIN出现低到高翻转时为1
// 否则默认为1
assign clk_ctrl = reg_ctrl[2] ? edge_detect : 1'b1;
// Enable selection
// 如果ctrl[1]=1,则EXTIN为高时为1
// 否则默认为1
assign enable_ctrl = reg_ctrl[1] ? ext_in_sync2 : 1'b1;
// Overall decrement control
// dec_ctrl需要综合上述逻辑给出计数器能否计数的结论
assign dec_ctrl = reg_ctrl[0] & enable_ctrl & clk_ctrl;
// Decrement counter,给出cur_val在下一个时钟沿需要变成的值
always @(write_enable04 or PWDATA or dec_ctrl or reg_curr_val or reg_reload_val)
begin
if (write_enable04)
nxt_curr_val = PWDATA[31:0]; // Software write to timer
else if (dec_ctrl)
begin
if (reg_curr_val == {32{1'b0}})
nxt_curr_val = reg_reload_val; // Reload
else
nxt_curr_val = reg_curr_val - 1'b1; // Decrement
end
else
nxt_curr_val = reg_curr_val; // Unchanged
end
// Interrupt generation
// Trigger an interrupt when decrement to 0 and interrupt enabled
// and hold it until clear by software
// 当计数器可以计数(dec_ctrl),中断使能(CTRL[3]),cur_value=0时,需要assert interrupt。
assign timer_int_set = (dec_ctrl & reg_ctrl[3] & (reg_curr_val==32'h00000001));
// 如果外界要将INTCLR寄存器写1,就需要清除中断信号
assign timer_int_clear = write_enable0c & PWDATA[0];
// 无论是生成还是清除,都需要更新INT寄存器
assign update_timer_int= timer_int_set | timer_int_clear;
// Registering interrupt output
always @(posedge PCLK or negedge PRESETn)
begin
if (~PRESETn)
reg_timer_int <= 1'b0;
else if (update_timer_int)
// 注意标准里提到,如果需要清除旧的中断信号时需要assert一个新的中断
// 则会生成新的中断信号,这个清除不能清除新的中断信号
reg_timer_int <= timer_int_set;
end
// Connect to external
assign TIMERINT = reg_timer_int;
endmodule
参考博客:
https://blog.csdn.net/fenggang2333/article/details/127793898