AMBA总线协议-结合ahb-master对ahb协议的理解

本文参考了github上的项目和网络上的其他资料,githbu原项目链接如下:

GitHub - seabeam/yuu_ahb: UVM AHB VIP (githubjs.cf)

1.对ahb-driver行为的理解:

由于ahb协议存在address phase和data phase,因此在设计driver的时候需要考虑到这一因素,提供三种方式来解决:

(1)将整个burst的数据传输分为三部分,也就是一个burst传输的第一拍,中间传输过程,burst传输的最后一拍(这种方式是我自己想的,比较笨,仅参考)。

task transfer();
	@(negedge hclk);
	wait(hready);
	@(posedge hclk);
	#0.1;
	hsel = 'b1;
	htrans = 'h2;
	hsize = size;
	hburst = burst;
	hwrite = write;
	haddr = addr;
	
	mid_transfer();
	
	@(negedge hclk);
	wait(hready);
	@(posedge hclk);
	#0.1;
	
	hsel = 'b0;
	htrans = 'h0;
	hsize = 'h0;
	hburst = 'h0;
	haddr = 'h0;
	if(hwrite)begin
		hwdata = hwdata_q[last];
	end
	else begin
		hrdata_q[last] = hrdata;
	end

endtask



task mid_transfer();
	int i = 0;
	repeat(len-1)begin
		@(negedge hclk);
		wait(hready);
		@(posedge hclk);
		#0.1;
		if(hburst == 'd2 || hburst == 'd4 || hburst == 'd8)begin
			if(hwrite)begin
				hwdata = hwdata_q[i];
				haddr =  (haddr&(~j)) + ((haddr+(8<<hszie)/8)&j);
				htrans = 'h3;
			end
			else begin
				hrdata_q[i] = hwdata;
				haddr =  (haddr&(~j)) + ((haddr+(8<<hszie)/8)&j);
				htrans = 'h3;
			end
		end
		else if(hburst == 'd2 || hburst == 'd4 || hburst == 'd8)begin
			if(hwrite)begin
				hwdata = hwdata_q[i];
				haddr =  haddr+(8<<hszie)/8;
				htrans = 'h3;
			end
			else begin
				hrdata_q[i] = hrdata;
				haddr =  haddr+(8<<hszie)/8;
				htrans = 'h3;
			end
		end
		i = i + 1;	
	end
	i = 0
endtask


int i = 0;
int j = 1;
int k = 0;

i = (hburst>>1)+hsize + 1;
repeat (i-1)begin
	j = (j<<1)+1; 
end 

if(hburst == 'd2 || hburst == 'd4 || hburst == 'd8)begin
	len = (2<<(hburst/2));
end
else if(hburst == 'd3 || hburst == 'd5 || hburst == 'd7)begin
	len = (2<<(hburst-1)/2);
end

(2)github上的项目,个人觉得这个项目的vip写的很好了,用到的ahb协议版本比较高,这里截取了核心部分(关于ahb2,3的部分)进行解释说明:

首先看下item的相关参数是如何设置的:

如果是incr类型,这里的incr类型包含incr/incr4/incr8/incr16,这里的aligned_address在字节对齐的情况下实际上就可以认为是start_address.

  aligned_address = (yuu_ahb_addr_t'(start_address/number_bytes))*number_bytes;
address[0] = start_address;
if (burst_type == AHB_INCR || burst_type == AHB_FIXED) begin
    low_boundary  = start_address;
    high_boundary = aligned_address+number_bytes*burst_length;
    for (int n=1; n<burst_length; n++) begin
      address[n] = aligned_address+n*number_bytes;
    end
  end
constraint c_ahb_align {
    address_aligned_enable == True;
    burst_type == AHB_WRAP -> address_aligned_enable == True;
    if (address_aligned_enable) { 
      burst_size == BYTE2   -> start_address[0]   == 1'b0;
      burst_size == BYTE4   -> start_address[1:0] == 2'b0;
      burst_size == BYTE8   -> start_address[2:0] == 3'b0;
      burst_size == BYTE16  -> start_address[3:0] == 4'b0;
      burst_size == BYTE32  -> start_address[4:0] == 5'b0;
      burst_size == BYTE64  -> start_address[5:0] == 6'b0;
      burst_size == BYTE128 -> start_address[6:0] == 7'b0;
    }
  }

需要注意上面计算high_bodunary这样做的目的在于保证地址字节对齐,因为协议中规定对于一个burst传输来说,必须保证字节对齐:

另外,需要注意的是对于byte访问的时候(或者说数据位宽低于总线位宽的时候),协议中的规定如下,也就是hwdata的发送mater随便发,slave去选择具体哪个数据有效,而对于hrdata来说,需要master去选有效数据。

 对于回环操作,地址选择如下:

这里计算了回环的边界,对于low_boundary,由初始地址/(burst长度*size对应的几个byte)。这里举个例子:比如需要进行一次word,wrap16传输,初始地址是’h68,那么计算过程就是104/(16*4)=1,1*64 = 64,计算得出初始地址是64.接下来算回卷地址的边界,也就是64+4*16 = 128.16进制是‘h80.

这里分了回环的第一次循环和接下来的循环,当是回环的第一次循环的时候is_wrapped值为false。第一次循环持续到地址加到边界处,此时触发下面的if条件语句,使得这次的地址并不是哪个边界值而是回环到初始值处,将is_wrap拉高,同时记录wrap_indx。此后由于is_wrap拉高使得进入第一个if条件中的else循环,每次地址递增会减去wrap_index值,进而实现递增。

else if (burst_type == AHB_WRAP) begin
    boolean is_wrapped = False;
    int wrap_idx = 0;

    wrap_boundary = (yuu_ahb_addr_t'(start_address/(number_bytes*burst_length)))*(number_bytes*burst_length);
    low_boundary  = wrap_boundary;
    high_boundary = low_boundary+number_bytes*burst_length;
    for (int n=1; n<burst_length; n++) begin
      if (!is_wrapped)
        address[n] = aligned_address+n*number_bytes;
      else
        address[n] = low_boundary + (n-wrap_idx)*number_bytes;

      if (address[n] == high_boundary) begin
        address[n] = wrap_boundary;
        wrap_idx = n;
        is_wrapped = True;
      end
    end
  end

此外,还可以在item中记录传输的位置:

function void yuu_ahb_item::command_process();
  // Location
  location = new[len+1];
  foreach (location[i])
    location[i] = MIDDLE;
  location[0]   = FIRST;
  location[len] = LAST;
endfunction

最后一点需要注意的是:考虑到不能跨越1kbyte的条件所以在给约束的时候需要增加如下条件:

constraint c_ahb_1k_boundary {
    if (burst_type == AHB_INCR) {
      start_address[9:0]+(len+1)*number_bytes <= 1024;
    }
    // WRAP never cross 1K
  }

需要指出的是跨越1kbyte这个条件仅仅存在incr传输中,在回环操作中是不会存在这种情况的。

接下来看driver:

主要的框架函数就是下面的部分,这里是把cmd_phase和data_phase分为两个任务放在一个fork-join any中。

task yuu_ahb_master_driver::get_and_drive();
  uvm_event handshake = events.get($sformatf("%s_driver_handshake", cfg.get_name()));
  process proc_drive;

  forever begin
    wait(vif.drv_mp.hreset_n === 1'b1);
    fork
      begin
        yuu_ahb_master_item item;

        proc_drive = process::self();
        processes["proc_drive"] = proc_drive;
        seq_item_port.get_next_item(item);
        handshake.trigger();
        @(vif.drv_cb);
        out_driver_port.write(item);
        `uvm_do_callbacks(yuu_ahb_master_driver, yuu_ahb_master_driver_callback, pre_send(this, item));
        fork
          cmd_phase(item);
          data_phase(item);
        join_any
        seq_item_port.item_done();
        handshake.reset();
      end
    join
  end
endtask

先看cmd_phase的任务:

在等待一定时间周期后将write,size,burst等传输过程中不变的信息驱动到总线上。

    repeat(cur_item.idle_delay) vif.wait_cycle();
    `uvm_info("cmd_phase", "Transaction start", UVM_HIGH)

    vif.drv_cb.hwrite <= cur_item.direction;
    vif.drv_cb.hsize <= cur_item.size;
    vif.drv_cb.hburst <= cur_item.burst;
    vif.drv_cb.hprot <= {cur_item.prot3, cur_item.prot2, cur_item.prot1, cur_item.prot0};
    vif.drv_cb.hprot_emt <= {cur_item.prot6_emt, cur_item.prot5_emt, cur_item.prot4_emt, cur_item.prot3_emt};
    vif.drv_cb.hmaster <= cur_item.master;
    vif.drv_cb.hmastlock <= cur_item.lock;
    vif.drv_cb.hnonsec <= cur_item.nonsec;
    vif.drv_cb.hexcl <= cur_item.excl;

下面开始循环,这个vip考虑了传输错误的情况,如果传输错误,会将事件error_stopped驱动,那么在传输地址前通过is_on捕捉到传输错误事件,那么这个循环会直接中止。 

for (int i=0; i <= len; i++) begin
//error operation:if error stop event is triggered ,that means the transfer is error,and the transfer shoud be terminate      
	  if (error_stopped.is_on()) begin
        if (error_key) begin
          error_stopped.reset();
          error_key = False;
        end
        else
          error_key = True;
        break;//terminate the transfer
      end

      drive_cmd_begin.trigger();
      `uvm_info("cmd_phase", "Beat start", UVM_HIGH)

在传输没有发送错误的前提下将地址驱动到总线上:

//haddr send 
      vif.drv_cb.haddr <= cur_item.address[i];

接下来需要考虑htrans,对于htrans来说,如果是busy传输,模拟master内部存在延迟,根据延迟周期进行wait_cycle.

//htrans send,there is two situation,one is busy,if htrans is busy,that means master should wait cycle	  
      if (cur_item.busy_delay[i] > 0) begin
        vif.drv_cb.htrans <= BUSY;
        repeat(cur_item.busy_delay[i]) vif.wait_cycle();
      end
	  
      vif.drv_cb.htrans <= cur_item.trans[i];

需要指出的是协议中明确规定了,只有在不定长的数据传输的时候才能用busy状态(也就是在传输命令阶段插入idle或者NONSEQ)。

 此外协议还规定:定长的传输类型中不能以busy结尾,single传输之后不能跟busy必须跟idle。

 协议的这一部分体现同样是在item中体现的,但是这里是放在mater item中体现的,如下:

function void yuu_ahb_master_item::command_process();
  super.command_process();

  // Trans
  trans = new[len+1];
  foreach (trans[i])
    trans[i] = SEQ;
  trans[0] = NONSEQ;
  if (!cfg.use_busy_end || (cfg.use_busy_end && burst != INCR)) begin
    busy_delay[len] = 0;
  end
  busy_delay[0] = 0;
endfunction

此外在master item中还有对是否由busy传输及busy传输需要等待的周期进行了约束,如下:

constraint c_busy {
    busy_delay.size() == len+1;
    foreach (busy_delay[i]) {
      soft busy_delay[i] inside {[0:`YUU_AHB_MAX_DELAY]};
      if (!cfg.busy_enable || len == 0) {
        busy_delay[i] == 0;
      }
    }
  }

接下来继续看driver,driver中需要考虑hready,如果hready为0的化,那么所有的信息都保留不变。 

//attention:whether data phase and address phase,when the hready is low,the information shoud be stay unchaged	  
      do
        vif.wait_cycle();
      while (vif.drv_cb.hready_i !== 1'b1);

上述的过程的整体结构包含:基本信息+haddr+htrans(考虑了busy)+hready。这个过程会持续到len+1次(从0开始计数),在最后一个周期的时候,实际上是最后一拍的数据阶段,此时addr等信息需要清0,通过item中位置信息来判断是否是最后的一个周期,如果是的化,将这些信息清0. 

//for the last cycle,cmd is send overed,but there is another data phase cycle,so the htrans/hmastlock shoud be rst
      if (cur_item.location[i] == LAST) begin
        vif.drv_cb.htrans <= IDLE;
        vif.drv_cb.hmastlock <= 1'b0;
        vif.drv_cb.hnonsec <= 1'b1;
      end

      drive_cmd_end.trigger();
      `uvm_info("cmd_phase", "Beat end", UVM_HIGH)
    end
  end

注意从上面的操作来看,hmastlock是在最后一个数据的地址段结束的时候拉低,这个过程也是符合协议的,协议的时序如下:

(个人觉得这个hmastelock和axi的原子操作很像,都是先读后写) 。bus被lock住需要关注hmastlock/hready/hsel三个信号,而被解除lock仅仅需要关注hmastlock和hready信号。协议中建议在lock传输结束的时候插入一个idle。

接下来看data_phase.

为了保证address phase和data phase的pipline结构,这里进行了如下操作:

 while (vif.drv_cb.hready_i !== 1'b1 || vif.mon_cb.htrans !== NONSEQ)
      vif.wait_cycle();
    `uvm_info("data_phase", "Transaction start", UVM_HIGH)
    drive_data_begin.trigger();

也就是说只有等到总线上已经出现了hready为高同时htrans为nonseq的时候才会进入到data phase。

在上述条件满足的条件下,进行循环过程:

下面是写操作:

for (int i=0; i <= len; i++) begin
      boolean has_got = False;
      `uvm_info("data_phase", "Beat start", UVM_HIGH)

//for write,give data
      if (cur_item.direction == WRITE) begin
        vif.drv_cb.hwdata <= cur_item.data[i];
        vif.drv_cb.upper_byte_lane <= cur_item.upper_byte_lane[i];
        vif.drv_cb.lower_byte_lane <= cur_item.lower_byte_lane[i];
      end

注意写操作和读操作对待ready为0的情况是不同的:

也就是说,对于写操作,hready拉低的那一拍,数据就可以放在总线上,而对于读操作来水,由于数据是由slave那一侧回来的,因此在这一拍拉低的时候是不能同一时间放在总线上,对于slave来说是在那高hready的那一周期同时将数据放到总线上,因此master要采集该数据的化需要等hready拉高的下一周期才可以将数据采集到。

 因此对于读数据有两种情况,第一种考虑ready信号为低的周期,仅仅当hready拉高后的下一个时钟沿才会去取数据(通过drv_cb采集的,用mon_cb应该也可以)

这里实际上还考虑了一种情况,就是如果此时master为busy状态,但是此时hrdata来了,那么此时是需要将这个回来的读数据收集的。

  do begin
        vif.wait_cycle(); 
    if (vif.drv_cb.hready_i === 1'b1 && cur_item.direction == READ && !has_got) begin
          if (i == 0)
            cur_item.exokay = vif.drv_cb.hexokay;
          cur_item.data[i] = vif.drv_cb.hrdata;
          cur_item.response[i] = vif.drv_cb.hresp;
          has_got = True;
        end
      end
      while (vif.drv_cb.hready_i !== 1'b1 || vif.mon_cb.htrans === BUSY);

上面的语句保证了这一 周期hready为高,如果为低的化,上面的语句就会收集数据,同时把has_got变为true,保证下面不会再次采集数据,如果hready直接为高,由于has_got为0,那么进入下面的采集数据过程。

 if (!has_got) begin
        if (cur_item.direction == READ) begin
          cur_item.data[i] = vif.drv_cb.hrdata;
        end
        cur_item.response[i] = vif.drv_cb.hresp;
        if (i == 0)
          cur_item.exokay = vif.drv_cb.hexokay;
      end

整体的driver过程就是上面的部分,个人觉得在data_phase阶段的数据循环用i<len即可。因为data_phase实际上是晚了一拍才开始执行的,因此仅仅需要len次循环就可以了。但是这里用来fork join_any,也就是地址段的最后一周期传输结束后就直接item_done了,因此data_phase的最后一个周期应该会被提前终止。这里的分析等以后有机会在论证。

关于monitor

monitor的整体框架如下:

task yuu_ahb_master_monitor::run_phase(uvm_phase phase);
  process proc_monitor;

  init_component();
  fork
    forever begin
      wait(vif.mon_mp.hreset_n === 1'b1);
      fork
        begin
          proc_monitor = process::self();
          processes["proc_monitor"] = proc_monitor;
          fork
            cmd_phase();
            data_phase();
          join_any
        end
      join
    end
    count_busy();
    count_idle();
    wait_reset();
  join
endtask

首先看cmd_phase:

一开始如果ready信号为低的化就等一拍。

  while (vif.mon_cb.hready_i !== 1'b1)
    vif.wait_cycle();

下面的逻辑实际上是为了收集传输过程中的item。 首先看while循环语句,也就是仅仅在hready为1并且htrans为NOSEQ或者idle的时候执行。也就是第一次来到NOSEQ并不是立马能够收集。这里收集的是传输过程中的内容。在传输过程中将addr和htrans推到队列中。这个过程会一直持续到将所有地址和htrans均收集完。

  if (address_q.size()>0 && (vif.mon_cb.htrans == NONSEQ || vif.mon_cb.htrans == IDLE))
    assembling_and_send(monitor_item);

  while(vif.mon_cb.hready_i !== 1'b1 || (vif.mon_cb.htrans !== NONSEQ && vif.mon_cb.htrans !== IDLE)) begin
    if (vif.mon_cb.hready_i === 1'b1 && vif.mon_cb.htrans === SEQ) begin
      address_q.push_back(vif.mon_cb.haddr);
      trans_q.push_back(yuu_ahb_trans_e'(vif.mon_cb.htrans));
    end
    vif.wait_cycle();
  end

第一次来的地址实际上是在这里收集。第一次来的地址由于是noseq,因此这里会先创建一个item,随后一些传输过程中不变的信息存入,随后将htrans和item存入。

monitor_cmd_begin.trigger();
  if (vif.mon_cb.htrans === NONSEQ) begin
    if (address_q.size()>0)
      assembling_and_send(monitor_item);
    monitor_item = yuu_ahb_master_item::type_id::create("monitor_item");
    `uvm_do_callbacks(yuu_ahb_master_monitor, yuu_ahb_master_monitor_callback, pre_collect(this, monitor_item));

    monitor_item.direction = yuu_ahb_direction_e'(vif.mon_cb.hwrite);
    monitor_item.size = yuu_ahb_size_e'(vif.mon_cb.hsize);
    monitor_item.burst = yuu_ahb_burst_e'(vif.mon_cb.hburst);
    monitor_item.prot3 = yuu_ahb_prot3_e'(vif.mon_cb.hprot[3]);
    monitor_item.prot2 = yuu_ahb_prot2_e'(vif.mon_cb.hprot[2]);
    monitor_item.prot1 = yuu_ahb_prot1_e'(vif.mon_cb.hprot[1]);
    monitor_item.prot0 = yuu_ahb_prot0_e'(vif.mon_cb.hprot[0]);
    monitor_item.prot6_emt = yuu_ahb_emt_prot6_e'(vif.mon_cb.hprot_emt[6]);
    monitor_item.prot5_emt = yuu_ahb_emt_prot5_e'(vif.mon_cb.hprot_emt[5]);
    monitor_item.prot4_emt = yuu_ahb_emt_prot4_e'(vif.mon_cb.hprot_emt[4]);
    monitor_item.prot3_emt = yuu_ahb_emt_prot3_e'(vif.mon_cb.hprot_emt[3]);
    monitor_item.master = vif.mon_cb.hmaster  ;
    monitor_item.lock = vif.mon_cb.hmastlock;
    monitor_item.nonsec = yuu_ahb_nonsec_e'(vif.mon_cb.hnonsec);
    monitor_item.excl = yuu_ahb_excl_e'(vif.mon_cb.hexcl);

    monitor_item.burst_size = yuu_ahb_burst_size_e'(monitor_item.size);
    if (monitor_item.burst inside {WRAP4, WRAP8, WRAP16})
      monitor_item.burst_type = AHB_WRAP;
    else
      monitor_item.burst_type = AHB_INCR;
    monitor_item.address_aligned_enable = True;

    monitor_item.start_time = $realtime();
    address_q.push_back(vif.mon_cb.haddr);
    trans_q.push_back(yuu_ahb_trans_e'(vif.mon_cb.htrans));
  end

  vif.wait_cycle();
  monitor_cmd_end.trigger();

  m_cmd_sem.put();

在第一次传输结束后由于前面整体框架中使用的forever语句,因此又会跳到前面的判断address_q是否为空,此时由于第一次的数据已经推到address_q中,因此会满足条件。注意这里的条件是如果address_q的size大于0同时htrans为noseq或者idle的情况。也就是说这里实际包含了两种情况。假设一次传输burst传输noseq传输之后由于burst传输还没有结束,因此还会继续执行前面的while循环语句。但是由于burst传输过程中不会再出现htrans为noseq的情况,因此不会再调用前面的“收集第一个地址”的语句。前面while语句会执行到整个burst传输全部收集结束,burst传输收集结束后会有两种情况,第一种是后面继续的传输,此时htrans应该为idle,另一种是后面紧跟着一个burst传输,此时htrans应该为noseq。无论是哪种情况,一个burst传输结束后都应该将数据打包发送出去,因此这里在满足这两种情况下调用assembling_and_send任务。

if (address_q.size()>0 && (vif.mon_cb.htrans == NONSEQ || vif.mon_cb.htrans == IDLE))
    assembling_and_send(monitor_item);

assembling_and_send任务如下(这个任务主要是将检测到的数据打包发送给别的地方):

首先根据address_q队列的大小对len进行赋值,注意这里的len并不是AHB总线上传输数据多少的意思,而是起到一个需要打包多少次的作用。

task yuu_ahb_master_monitor::assembling_and_send(yuu_ahb_master_item monitor_item);
  int len = address_q.size()-1;
  yuu_ahb_master_item item = yuu_ahb_master_item::type_id::create("monitor_item");

  #0;
  item.copy(monitor_item);
  item.len = len;
  item.address = new[len+1];
  item.data = new[len+1];
  item.trans = new[len+1];
  item.response = new[len+1];

  item.busy_delay = new[len+1];
  item.location = new[len+1];

具体的打包过程如下:在第一次中由于len值为0,因此实际上只会打包一次。打包的过程也就是将前面收集到的address推到item中。这里还有data是为了下面再data phase中同样需要打包。

for (int i=0; i<=len; i++) begin
    item.address[i] = address_q.pop_front();
    item.data[i] = data_q.pop_front();
    item.trans[i] = trans_q.pop_front();
    item.response[i] = response_q.pop_front();

    item.busy_delay[i] = busy_q.pop_front();
  end
  item.exokay = exokay_q.pop_front();
  item.idle_delay = idle_q.pop_front();

 

同时len产生位置信息:

 foreach (item.location[i])
    item.location[i] = MIDDLE;
  item.location[0] = FIRST;
  item.location[len] = LAST;

  item.start_address = item.address[0];

  item.end_time = $realtime();

  `uvm_do_callbacks(yuu_ahb_master_monitor, yuu_ahb_master_monitor_callback, post_collect(this, item));

  out_monitor_port.write(item);
//  item.print();
endtask

接下来看data phase阶段:

data phase阶段较为简单,前面类似等ready、等noseq保证pipline信号。

task yuu_ahb_master_monitor::data_phase();
  uvm_event monitor_data_begin = events.get($sformatf("%s_monitor_data_begin", cfg.get_name()));
  uvm_event monitor_data_end   = events.get($sformatf("%s_monitor_data_end", cfg.get_name()));

  m_data_sem.get();
  while (vif.mon_cb.hready_i !== 1'b1 || (vif.mon_cb.htrans !== NONSEQ && vif.mon_cb.htrans !== SEQ))
    vif.wait_cycle();
  do
    vif.wait_cycle();
  while (vif.mon_cb.hready_i !== 1'b1);

 

接着根据传输的item是写还是读,将总线上的写数据或者是读数据分别推到队列上,同时还需要记录response。

 monitor_data_begin.trigger();

  if (monitor_item.direction == WRITE) begin
    data_q.push_back(vif.mon_cb.hwdata);
  end
  else if (monitor_item.direction == READ) begin
    data_q.push_back(vif.mon_cb.hrdata);
  end
  response_q.push_back(yuu_ahb_response_e'(vif.mon_cb.hresp));
  if (monitor_item.excl == EXCLUSIVE)
    exokay_q.push_back(yuu_ahb_exokay_e'(vif.mon_cb.hexokay));
  else if (exokay_q.size() == 0)
    exokay_q.push_back(EXOKAY);

  monitor_data_end.trigger();
  m_data_sem.put();
endtask

 最后回顾调用assembling_and_send的时间。在第一次地址收集的时候由于address_q的size为0,因此实际上别不会调用assembling_and_send。在address phase任务结束的时候打了一拍,打拍后此时总线上的htrans已经是seq。因此这里实际上在第二拍的同一时刻在data phase中会将data推入到队列中。但是实际上在进入到第二拍的时候实际上address phase由于forever的作用也进入到了第二次的循环,那么进入到address phase后根据前面的分析就会调用 assembling_and_send任务。那么你怎么保证是你先调用assembling_and_send任务快还是data phase中往队列中推入data的数据快呢?如果后者快,那么assembling_and_send的行为是正确的,可以把每一个address对应的data正好pipline的打包。但是如果前者快,那么实际这里就会出现错误。

注意assembling_and_send在进入到任务后有一个#0,这个#0保证了同一时刻data phase中往队列中推入data的数据是快于data phase中往队列中推入data的数据任务中调用打包逻辑。

(3)未完...

  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: AMBA 3 AHB-Lite是一种总线协议,用于在集成电路中连接不同模块或外设。AMBA(Advanced Microcontroller Bus Architecture)是ARM公司开发的一种标准总线,旨在促进系统级集成的设计。 AHB-Lite是AMBA 3总线规范的一部分,它是ARM公司为嵌入式系统提供的低功耗和高性能的解决方案。AHB-Lite的设计目标是提供一个简化版本的AHB总线,以满足对较小和简单系统的需求。 AHB-Lite总线采用了一种简化的总线传输方法,同时保留了AHB总线的一些重要特性和功能。它支持主从模式,其中一个设备充当主设备,能够发起数据传输请求,而其他设备作为从设备,能够应答主设备的请求。 AHB-Lite总线具有低功耗和低复杂性的特点,可以减少面积和功耗,并提供高性能的数据传输。它支持多个主设备和从设备的连接,并使用分时复用的方式进行数据传输。主设备可以通过读写传输和直接存储器访问(DMA)来实现数据交换。 AHB-Lite总线还支持优先级访问,可根据不同模块的需要分配不同的优先级,以确保高优先级的数据传输能够及时处理。此外,它还支持错误检测和纠正机制,以确保数据传输的可靠性和完整性。 总之,AMBA 3 AHB-Lite是一种低功耗、高性能和简化的总线协议,用于连接不同模块和外设,并提供可靠的数据传输和处理能力。它在嵌入式系统中广泛使用,以满足对小型和简单系统的要求。 ### 回答2: AMBA 3 AHB-Lite是一种轻量级、高性能的总线协议,用于连接处理器、内存和外设等不同设备,提供高效的数据传输和控制信号交互。 AMBA是ARM公司提出的一系列总线标准,AHB-Lite则是其中的一种。AHB-Lite是AMBA总线的一种精简版本,相对于更为复杂的AHB和APB总线AHB-Lite更加简单、易于实现和集成。 AHB-Lite总线结构包括主设备(如处理器)和从设备(如内存和外设),通过地址线、数据线、控制线和时钟信号等进行通信。主设备通过发起地址传输来读取或写入从设备的数据。AHB-Lite总线采用分片事务的方式,可以高效地支持多个主设备的并行访问。 AHB-Lite总线的特点之一是具有低功耗和低延迟。由于其采用有效的传输机制和轻量级的设计,能够在多个主设备之间快速传输数据,降低功耗消耗和延迟。 此外,AHB-Lite还支持多种访问模式,如单次传输、增量传输和乱序传输等。这些传输模式可以根据不同的应用场景和要求进行选择,提高系统的灵活性和性能。 总之,AMBA 3 AHB-Lite是一种高效、低功耗、低延迟的总线协议,适用于连接不同设备的通信和数据交互,为系统提供了快速、可靠的数据传输和控制机制。 ### 回答3: AMBA是一种应用于ARM处理器的总线架构,其中AMBA 3是第三代的AMBA总线标准。而AHB-Lite则是AMBA 3中的一种总线协议AMBA 3 AHB-Lite是一个高性能、低功耗的总线协议,适用于处理器和外围设备之间的通信。它具有如下特点: 1.简单易懂:AHB-Lite协议相对于AMBA 3中的其他总线协议来说,是比较简单的,因此易于理解和实现。这使得开发者可以更容易地集成和实现AHB-Lite接口。 2.低功耗:AHB-Lite设计了一些较低功耗的机制,如低功耗模式和动态功耗缩减技术,以帮助节省能源,并延长设备的待机时间。 3.高性能:尽管AHB-Lite是AMBA总线中较简单的协议,但它仍然提供了较高的性能,可以处理高速数据传输和多个总线事务同时发生的情况。因此,它适用于嵌入式系统中对性能要求较高的场景。 4.灵活可扩展:AHB-Lite具有良好的可扩展性,它支持连接多个处理器和外围设备,并允许通过添加信号进行系统扩展。这使得AHB-Lite适用于各种不同规模和配置的系统。 总之,AMBA 3 AHB-Lite是一种高性能、低功耗、简单易懂且灵活可扩展的总线协议,适用于ARM处理器和外围设备之间高效通信的嵌入式系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值