SWD硬件实现
最近还在研究SWD协议,前面一篇文章中我们已经讲到了SWD的基本协议,这篇文章我们来看一下这个协议的一个硬件实现。本次硬件设计采用Verilog,因为Verilog也已经扔下了很久了,所以代码质量一般,也没有做什么测试,这里只是简单的看了一下输出的波形,其它的暂时没有关心。
设计的总体思路
实际上整个SWD协议还是非常简单的,硬件实现也是通过简单的状态机就可以实现,这里也刚好回顾一下状态机的写法,毕竟上次写状态机似乎也是五六年之前了。
因为是硬件设计,这里也就着重实现最为底层的收发时序,对于包传输的状态判断这里就不做涉及了,所以在SWD包状态不做判断,另外也没有支持包的连续传输,这两个部分可以在后续改进或者有需求的时候再添加。
系统设计时,首先我们要定义一个硬件接口,这里的定义就非常简单了,输入信号主要有三个:传输包命令,传输包数据和命令有效的一个信号,输出信号则是包读取的数据,传输状态和传输完成信号,另外就是和端口相关的信号以及时钟和复位信号。
系统的整个传输过程通过一个状态机实现,每个状态传输特定的数据,为了简化设计,状态机状态划分比较细。
具体实现
这里就不详细说了,直接看看代码好了:
`timescale 1 ns / 100 ps
module swd(
// pad interface
input ipp_ind_sclk,
input ipp_ind_sdio,
output ipp_obe_sclk,
output ipp_obe_sdio,
output ipp_do_sclk,
output ipp_do_sdio,
// command interface
input clk,
input rst_n,
input [3:0] cmd,
input [31:0] wdata,
input cmd_valid,
output [31:0] rdata,
output [2:0] ack,
output read_error,
output cmd_done
);
// next_state machine
parameter IDLE = 5'd0;
parameter RESET_HIGH = 5'd1;
parameter RESET_LOW = 5'd2;
parameter REQUEST_START = 5'd3;
parameter REQUEST_CMD = 5'd4;
parameter REQUEST_PARITY = 5'd5;
parameter REQUEST_STOP = 5'd6;
parameter REQUEST_PARK = 5'd7;
parameter REQUEST_TRN = 5'd8;
parameter ACK = 5'd9;
parameter ACK_TRN = 5'd10;
parameter WRITE = 5'd11;
parameter WRITE_PARITY = 5'd12;
parameter READ = 5'd13;
parameter READ_PARITY = 5'd14;
parameter READ_TRN = 5'd15;
parameter DONE = 5'd16;
parameter CNT_WIDTH = 7;
reg [4:0] state = IDLE;
reg [4:0] next_state = IDLE;
reg ipp_obe_sclk;
reg ipp_obe_sdio;
wire ipp_do_sclk;
reg ipp_do_sdio;
reg [31:0] rdata;
reg [2:0] ack;
reg read_error;
reg cmd_done;
reg [3:0] cmd_buffer;
reg [31:0] write_data;
reg [CNT_WIDTH - 1:0] cnt;
reg parity_bit;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
next_state <= IDLE;
end
else
case(next_state)
IDLE: begin
ipp_obe_sclk <= 1'b0;
ipp_obe_sdio <= 1'b0;
cmd_done <= 1'b1;
cnt <= 'd0;
parity_bit <= 1'b0;
if (cmd_valid) begin
cmd_buffer <= cmd;
write_data <= wdata;
next_state <= (&cmd) ? RESET_HIGH : REQUEST_START;
end else
next_state <= IDLE;
end
RESET_HIGH: begin
cmd_done <= 1'b0;
read_error <= 1'b0;
ack <= 3'b000;
rdata <= 32'h0;
ipp_obe_sclk <= 1'b1;
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= 1'b1;
next_state <= cnt[6] ? RESET_LOW : RESET_HIGH;
cnt <= cnt + 1'b1;
end
RESET_LOW: begin
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= 1'b0;
next_state <= cnt[2] ? IDLE : RESET_LOW;
cnt <= (cnt + 1'b1);
end
REQUEST_START: begin
// send START bit
cmd_done <= 1'b0;
ack <= 3'b000;
rdata <= 32'h0;
read_error <= 1'b0;
ipp_obe_sclk <= 1'b1;
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= 1'b1;
parity_bit <= 1'b0;
next_state <= REQUEST_CMD;
end
REQUEST_CMD: begin
// send APnDP, RnW, A[2:3]
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= cmd_buffer[3];
cmd_buffer = {cmd_buffer[2:0], cmd_buffer[3]};
cnt <= cnt + 1'b1;
next_state <= (cnt == 6'd3) ? REQUEST_PARITY : REQUEST_CMD;
end
REQUEST_PARITY: begin
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= ^cmd_buffer;
cnt <= 'd0;
next_state <= REQUEST_STOP;
end
REQUEST_STOP: begin
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= 1'b0;
cnt <= 'd0;
next_state <= REQUEST_PARK;
end
REQUEST_PARK: begin
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= 1'b1;
cnt <= 'd0;
next_state <= REQUEST_TRN;
end
REQUEST_TRN: begin
ipp_obe_sdio <= 1'b0;
ipp_do_sdio <= 1'b0;
cnt <= 'd0;
next_state <= ACK;
end
ACK: begin
ack <= {ack[1:0], ipp_ind_sdio};
cnt <= cnt + 1'b1;
next_state <= (cnt == 'd2) ? (cmd_buffer[2] ? READ:ACK_TRN) : ACK;
cnt <= (cnt == 'd2) ? 'd0 : (cnt + 1'b1);
parity_bit <= 1'b0;
end
ACK_TRN: begin
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= 1'b0;
cnt <= 'd0;
next_state <= WRITE;
end
WRITE: begin
ipp_obe_sdio <= 1'b1;
cnt <= cnt + 1'b1;
parity_bit <= parity_bit ^ write_data[0];
write_data <= {1'b0, write_data[31:1]};
ipp_do_sdio <= write_data[0];
next_state <= cnt[5] ? WRITE_PARITY : WRITE;
end
WRITE_PARITY: begin
ipp_obe_sdio <= 1'b1;
ipp_do_sdio <= parity_bit;
cnt <= 'd0;
next_state <= DONE;
end
READ: begin
ipp_obe_sdio <= 1'b0;
cnt <= cnt + 1'b1;
parity_bit <= parity_bit ^ ipp_ind_sdio;
rdata <= {ipp_ind_sdio, rdata[31:1]};
next_state <= cnt[5] ? READ_PARITY : READ;
end
READ_PARITY: begin
ipp_obe_sdio <= 1'b0;
read_error <= (ipp_do_sdio == parity_bit);
cnt <= 'd0;
next_state <= DONE;
end
DONE: begin
cnt <= cnt + 1'b1;
ipp_obe_sdio <= 1'b0;
next_state <= cnt[4] ? IDLE : DONE;
end
default:
next_state <= IDLE;
endcase
state <= next_state;
end
assign ipp_do_sclk = clk;
endmodule
没错,我就是这么任性,一句注释也没有,啊哈哈,不过代码简单易懂,就酱。
最后更新于 2020-03-14 09:30:57 并被添加「FPGA Verilog SWD」标签,已有 7975 位童鞋阅读过。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。