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

没错,我就是这么任性,一句注释也没有,啊哈哈,不过代码简单易懂,就酱。


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

相关文章

发表新评论