valid-ready握手协议 打拍方案(二)——skid buffer

上一篇文章介绍了valid-ready握手协议,及其三种打拍情况下的解决方案。

业界针对这种打拍的情况,已经有成熟的IP去解决这件事情了。本文以如下链接介绍的skid buffer为例,分析一下它的原理,本文提供一个快速了解的途径,感兴趣的还是推荐看一下原文。

Pipeline Skid Buffer (fpgacpu.ca)

 

前提:

skid buffer目的在于:解决valid-ready握手中,对valid, data和ready三条路径都进行打拍(以后简称“三路打拍”)的情况(不允许三条路径中出现组合逻辑直连),提高时序性能。

只对valid, data打拍,或只对ready打拍,需要一个buffer,此buffer通常为data路径的打拍寄存器,也可以使用depth为1的FIFO。(注意,对ready打拍时,往往是通过valid, data路打拍+passby直通来实现的,否则无法实现最大效率,因为若不使用passby直通,就等效于三路打拍了。)

若进行三路打拍,如果想要达到最大的传输效率,消除气泡,就需要至少两个buffer(原因可以想一下,我是通过FIFO试出来的,可以理解为由于valid->ready反馈回路打拍过多,需要寄存的数据就变多了)。同样,此buffer可以为任意形式,比如:depth为2的FIFO;两个打拍寄存器;ping-pong buffer。不管什么形式,本质都是寄存数据,并保证最大的传输效率。

skid buffer原理简介:

skid buffer使用了两个寄存器:buffer register和main register。可以看到,main register之前的部分(buffer path + passby path + mux),与 “只对ready打拍” 时的处理方法非常类似,都是:buffer中无数据,就直通;buffer中有数据,就使用buffer中的数据;如果receiver没有握手接收数据,那后面的数据就放到buffer里,否则就直通就完事了。由于不允许组合逻辑直通,在此基础上增加了一个main register,作为主buffer,而buffer register可以看作副buffer,只有当main register中的数据没有被receiver握手接收时,才把sender发过来的data放入buffer register。

skid buffer整体的思想就是如上所述,其具体代码实现,思路非常清晰,做到了像FIFO一样的,将skid buffer的工作机制与外部的信号解耦,可读性很好。

verilog实现:

skid buffer模块的接口不用多说,就是实现了一个通用的IP,去处理上下游的握手信号和数据信号。 

data path: 

比较好理解,描述了两个buffer和一个mux,都设置了控制信号。所有的工作就是在于何时去使能这些控制信号,来实现数据的流控。

    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            buf_data <= {DATA_WIDTH{1'b0}};
        end else if(buf_data_wren) begin
            buf_data <= in_data;
        end
    end

    assign out_data_pre = (use_buf_data) ? buf_data : in_data;

    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            out_data <= {DATA_WIDTH{1'b0}};
        end else if(out_data_wren) begin
            out_data <= out_data_pre;
        end
    end
control path:

skid buffer使用状态机去控制,共有三个状态:EMPTY, BUSY, FULL。

  • empty:main reg, buffer reg都无data
  • busy:main reg有data,buffer reg无data
  • full:main reg, buffer reg都有data 

整个skid buffer对于外部可以抽象成一个黑盒,上游握手成功会使其内部data数量 + 1, 下游握手成功会使其内部data数量 - 1。

assign insert = in_vld && in_rdy;
assign remove = out_vld && out_rdy;

assign incr = (insert == 1'b1) && (remove == 1'b0);
assign decr = (insert == 1'b0) && (remove == 1'b1);
assign cons = (insert == 1'b1) && (remove == 1'b1);

根据不同的状态,以及data数量变化,可以归纳出若干操作,就像上面的状态转移图所示,不同的操作导致的不同的状态跳转。 附上官方对每个信号的解释。(CBM涉及到的两个信号之后会提到)。其实,就是将我们所说的,何时passby,何时装入buffer,进行了规定。

assign load   = (state_cur == EMPTY) && incr;
assign flow   = (state_cur == BUSY) && cons;
assign fill   = (state_cur == BUSY) && incr;
assign unload = (state_cur == BUSY) && decr;
assign flush  = (state_cur == FULL) && decr;

 

 状态的跳转明晰了,就可以写出来代码:

always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        state_cur <= EMPTY;
    end else begin
        state_cur <= state_nxt;
    end
end

always@(*) begin
    case(state_cur)
        EMPTY:  begin
                    if(incr == 1'b1)    
                        state_nxt = BUSY;
                    else
                        state_nxt = state_cur;
                end
        BUSY:   begin
                    if(incr == 1'b1)   
                        state_nxt = FULL;
                    else if(decr == 1'b1)
                        state_nxt = EMPTY;
                    else 
                        state_nxt = state_cur;
                end
        FULL:   begin
                    if(decr == 1'b1)    
                        state_nxt = BUSY;
                    else 
                        state_nxt = state_cur;
                end
        default:begin
                    state_nxt = state_cur;
                end
    endcase
end

根据状态,给出寄存器输出的ready和valid信号。非空就可以读,非满就可以写。

always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        out_vld <= 1'b0;
    end else begin
        out_vld <= (state_nxt == BUSY) || (state_nxt == FULL);
    end
end

always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        in_rdy <= 1'b0;
    end else begin
        in_rdy <= ((state_nxt == EMPTY) || (state_nxt == BUSY)); 
    end
end

现在,可以根据不同的操作去控制我们最初说的,data路的三个控制信号。可以看到状态机不一定非要按着三段式的模板来写,怎么方便怎么来,还是很灵活的。

assign buf_data_wren = (fill == 1'b1);
assign out_data_wren = (load == 1'b1) || (flow == 1'b1) || (flush == 1'b1);
assign use_buf_data = (flush == 1'b1);
CBM模式:

CBM是我参考的skid buffer中提出的一个模式,大体意思就是:正常来说,buffer满了就不会去写,否则会丢数据。但如果有这么一种情况:某个场景下,设计者就希望新数据可以覆盖buffer中的旧数据,那这时候就可以使能CBM模式。

采用CBM模式时,还是原来的三个状态,只不过多了两个信号:dump和pass

CBM模式仅对data path的三个控制信号、sender端的ready信号有所改变。

  • CBM模式只在full时,与普通模式不同。在full状态下,只要sender端握手成功,发送了数据,那么skid buffer就会把数据接收,并传给receiver,不会等待旧数据的传输,而是直接覆盖
  • CBM模式下,sender端的ready信号常为1,一直都可以接收数据。
always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        in_rdy <= 1'b0;
    end else begin
        in_rdy <= 1'b1;
    end
end

assign dump   = (state_cur == FULL) && incr;
assign pass   = (state_cur == FULL) && cons;

assign buf_data_wren = (fill == 1'b1) || (dump == 1'b1) || (pass == 1'b1);
assign out_data_wren = (load == 1'b1) || (flow == 1'b1) || (flush == 1'b1) || (dump == 1'b1) || (pass == 1'b1);
assign use_buf_data = (flush == 1'b1) || (dump == 1'b1) || (pass == 1'b1);

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
skid_steer是一种滑移转向(skid-steer)机器人的驱动系统。在构建机器人模型时,为了实现滑移转向的功能,需要添加skid_steer_drive_controller插件。插件的代码如下: ``` <gazebo> <plugin name="skid_steer_drive_controller" filename="libgazebo_ros_skid_steer_drive.so"> <alwaysOn>true</alwaysOn> <updateRate>100.0</updateRate> <robotNamespace>/</robotNamespace> <leftFrontJoint>base_to_wheel1</leftFrontJoint> <rightFrontJoint>base_to_wheel3</rightFrontJoint> <leftRearJoint>base_to_wheel2</leftRearJoint> <rightRearJoint>base_to_wheel4</rightRearJoint> <wheelSeparation>0.2</wheelSeparation> <wheelDiameter>0.1</wheelDiameter> <torque>2</torque> <commandTopic>cmd_vel</commandTopic> <odometryTopic>odom</odometryTopic> <odometryFrame>odom</odometryFrame> <robotBaseFrame>base_footprint</robotBaseFrame> <broadcastTF>1</broadcastTF> </plugin> </gazebo> ``` 在这个插件中,通过设置参数来定义驱动系统的行为。例如,torque参数设置轮子的扭矩力,wheelSeparation参数设置左右轮子之间的距离,wheelDiameter参数设置轮子的直径,commandTopic参数设置订阅的控制指令主题,odometryTopic参数设置机器人发布的里程计主题,odometryFrame参数设置odom坐标系,robotBaseFrame参数设置机器人基坐标系。 关于skid_steer驱动系统的原理,它与差速驱动系统有密切的关系。唯一的不同之处在于,滑移转向系统用两个额外的驱动轮代替了差速驱动的脚轮(通常称为万向轮)。滑移转向系统也有同样的缺陷,即直线运动要求所有轮子以相同速度转动,这将很难完成。然而,滑移转向系统的优点是具有较大的牵引力且没有“脚轮效应”。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【ROS】移动机器人导航仿真(2)——SLAM(gmapping)](https://blog.csdn.net/wangchao7281/article/details/53647481)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [机器人滑动转向驱动方式(Skid-steer Drive)](https://blog.csdn.net/github_30605157/article/details/52170006)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值