首页 > 其他分享 >AHBRAM框架

AHBRAM框架

时间:2023-05-28 15:22:05浏览次数:45  
标签:begin end 框架 burst data uvm AHBRAM type

 1.AHB传输

 

两个阶段

  • 地址阶段,只有一个cycle
  • 数据周期,由HREADY信号决定需要几个cycle 

流水线传送

  • 先是地址周期,然后是数据周期

2.AHB协议信号

 

3.工程架构


测试平台具体用的是RKV前缀的,VIP_LIB里面是lvc的前缀

利用AHB master VIP搭建验证环境:先实现基础的AHB master VIP,然后利用AHB VIP搭建验证环境并完成冒烟测试以及多个功能测试用例

  • rkv用的是rkv的接口,lvc用的当然是lvc的接口,如rkv_ahbram_base_virtual_sequence 用的接口是rkv_ahbram_if。当然他们不是相互独立的,有联系,是基于lvc的VIP,然后有RKV的一些功能,搭建出验证平台
  • lvc_driver里面按照协议的AHB时序和类型等要求实现了数据的读、写功能

由下图可知,工程中的elem_seqs和sequence_lib之间的关系,即elem_seqs把sequence_lib拿来用

 

 

 

4.接口方向

时钟块的方向

1.interface接口中信号的方向

首先记住一点:我们的目标都是围绕着验证DUT,因此都是接口中的信号方向是相对于DUT而言的

【以AHBRAM这个DUT来分析】

 

 

注:monitor中接受所有信号,全部有关的信号都可认为是输入信号

接口的时钟块分析是相对于DUT而言来分析的: cb_mst的input是DUT的反馈--即slave对master的反馈;output是TB对DUT的输入--即master对slave的操作 cb_slv则相反

 

为什么用时钟块clocking?

答:clocking块基于时钟周期对信号进行驱动或者采样的方式,使用clocking采样会比直接用接口中的clk更加稳定,但是在组合逻辑中不能用clocking。

目前是RTL仿真,只需解决RTL时序。同步逻辑或者组合逻辑都会插入延迟,因为是器件决定的。在门级仿真时真实的物理时序。


2.时钟块的用法

 

 

 

建议:接口信号并不是一定要用非阻塞赋值。因为你用了时钟块,所以非阻塞或者阻塞都没事。

 

5.代码解析

 

5.1 rkv_ahbram_if这个文件的作用
因为与dut和大部分组件连接的接口是lvc_ahb_if,所以rkv_ahbram_if接口的主要功能并不是连接。
在rkv_ahbram_if中定义了reset信号,作为硬件的驱动,reset信号放在这里可以方便后续测试的调用,可测试reset信号是否有效清空memory里面的数据

 

5.2 lvc_ahb_if 

`ifndef LVC_AHB_IF_SV
`define LVC_AHB_IF_SV

interface lvc_ahb_if;
  `include "lvc_ahb_defines.svh"
  import lvc_ahb_pkg::*;
//这两行导入是由于代码直接有联系,必须要用到
//比如`LVC_AHB_MAX_DATA_WIDTH定义在lvc_ahb_defines.svh
//比如response_type_enum定义在lvc_ahb_types.sv,而lvc_ahb_types.sv定义在lvc_ahb_pkg
  logic                                   hclk;
  logic                                   hresetn;

  logic                                   hgrant;
  logic [(`LVC_AHB_MAX_DATA_WIDTH - 1):0] hrdata;
  logic                                   hready;
  logic [1:0]                             hresp;
  logic [(`LVC_AHB_MAX_ADDR_WIDTH - 1):0] haddr;
  logic [2:0]                             hburst;
  logic                                   hbusreq;
  logic                                   hlock;
  logic [3:0]                             hprot;
  logic [2:0]                             hsize;
  logic [1:0]                             htrans;
  logic [(`LVC_AHB_MAX_DATA_WIDTH - 1):0] hwdata;
  logic                                   hwrite; 

  response_type_enum                      debug_hresp;
  trans_type_enum                         debug_htrans;
  burst_size_enum                         debug_hsize;
  burst_type_enum                         debug_hburst;
  xact_type_enum                          debug_xact;
  status_enum                             debug_status;

//特殊的类型转换语法:<target_type>'(<expression>)
//效果例如debug_hresp最终得到OKAY这个枚举元素

  // debug signals assignment
  assign debug_hresp    = response_type_enum'(hresp);
  assign debug_htrans   = trans_type_enum'(htrans);
  assign debug_hsize    = burst_size_enum'(hsize);
  assign debug_hburst   = burst_type_enum'(hburst);
  //为什么没有xact status,xact信号对应的是hwrite 可以通过monitor观察
  //status在此时只赋值了一个初值INITIAL(在transaction中),也不需要发送的数据更新
  // the below signals to be assigned by monitor
  // debug_xact ..
  // debug_status ..

  clocking cb_mst @(posedge hclk);
    // USER: Add clocking block detail
    default input #1ps output #1ps;
    output haddr, hburst, hbusreq, hlock, hprot, hsize, htrans, hwdata, hwrite; 
    input hready, hgrant, hrdata;
  endclocking : cb_mst

  clocking cb_slv @(posedge hclk);
   // USER: Add clocking block detail
    default input #1ps output #1ps;
    input haddr, hburst, hbusreq, hlock, hprot, hsize, htrans, hwdata, hwrite; 
    output hready, hgrant, hrdata;
  endclocking : cb_slv

  clocking cb_mon @(posedge hclk);
   // USER: Add clocking block detail
    default input #1ps output #1ps;
    input haddr, hburst, hbusreq, hlock, hprot, hsize, htrans, hwdata, hwrite; 
    input hready, hgrant, hrdata;
  endclocking : cb_mon

endinterface


`endif // LVC_AHB_IF_SV

 

5.3 lvc_ahb_master_driver

 

`ifndef LVC_AHB_MASTER_DRIVER_SV
`define LVC_AHB_MASTER_DRIVER_SV

class lvc_ahb_master_driver extends lvc_ahb_driver;
  lvc_ahb_agent_configuration cfg;
  virtual lvc_ahb_if vif;
  `uvm_component_utils(lvc_ahb_master_driver)

  ......

  virtual task drive_transfer(REQ t);
    // TODO implementation in child class
    case(t.burst_type)
      SINGLE: begin do_atomic_trans(t); end
      INCR  : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      WRAP4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      INCR4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      WRAP8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      INCR8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      WRAP16: begin `uvm_error("TYPEERR", "burst type not supported yet") end
      INCR16: begin `uvm_error("TYPEERR", "burst type not supported yet") end
      default: begin `uvm_error("TYPEERR", "burst type not defined") end
    endcase
  endtask

  virtual task do_atomic_trans(REQ t);
    case(t.xact_type)
      READ      : do_read(t);
      WRITE     : do_write(t);
      IDLE_XACT : begin `uvm_error("TYPEERR", "trans type not supported yet") end
      default: begin `uvm_error("TYPEERR", "trans type not defined") end
    endcase
  endtask

  virtual task wait_for_bus_grant();
    @(vif.cb_mst iff vif.cb_mst.hgrant === 1'b1);
  endtask

  virtual task do_write(REQ t);
    do_init_write(t);
    do_proc_write(t);
  endtask

  virtual task do_read(REQ t);
    do_init_read(t);
    do_proc_read(t);
  endtask

  virtual task do_init_write(REQ t);
    wait_for_bus_grant();
    @(vif.cb_mst);  
    vif.cb_mst.htrans <= NSEQ;
    vif.cb_mst.haddr  <= t.addr;
    vif.cb_mst.hburst <= t.burst_type;
    vif.cb_mst.hsize  <= t.burst_size;
    vif.cb_mst.hwrite <= 1'b1;
    @(vif.cb_mst);   
    vif.cb_mst.hwdata <= t.data[0];
    
    forever begin
      @(negedge vif.hclk);
      if(vif.hready === 1'b1) begin
        break;
      end
      else
        @(vif.cb_mst);    
    end

    // update current trans status
    t.trans_type = NSEQ;
    t.current_data_beat_num = 0; // start beat from 0 to make consistence with data array index
    t.all_beat_response[t.current_data_beat_num] = response_type_enum'(vif.hresp); 

  endtask

  virtual task do_init_read(REQ t);
    wait_for_bus_grant();
    @(vif.cb_mst);
    vif.cb_mst.htrans <= NSEQ;
    vif.cb_mst.haddr  <= t.addr;
    vif.cb_mst.hburst <= t.burst_type;
    vif.cb_mst.hsize  <= t.burst_size;
    vif.cb_mst.hwrite <= 1'b0;
    @(vif.cb_mst);
    // check ready with delay in current cycle
    forever begin
      @(negedge vif.hclk);
      if(vif.hready === 1'b1) begin
        break;
      end
      else
        @(vif.cb_mst);
    end
    t.data = new[t.current_data_beat_num+1](t.data);
    t.data[0] = vif.hrdata;   
    // update current trans status
    t.trans_type = NSEQ;
    t.current_data_beat_num = 0; // start beat from 0 to make consistence with data array index
    t.all_beat_response[t.current_data_beat_num] = response_type_enum'(vif.hresp);
  endtask

  virtual task do_proc_write(REQ t);
    // TODO implement for SEQ operations of other BURST types
    do_init_idle(t);
  endtask

  virtual task do_proc_read(REQ t);
    // TODO implement for SEQ operations of other BURST types
    do_init_idle(t);
  endtask

  virtual protected task do_init_idle(REQ t);
    @(vif.cb_mst);
    _do_drive_idle();
  endtask

  virtual protected task _do_drive_idle();
    vif.cb_mst.haddr     <= 0;
    vif.cb_mst.hburst    <= 0;
    vif.cb_mst.hbusreq   <= 0;
    vif.cb_mst.hlock     <= 0;
    vif.cb_mst.hprot     <= 0;
    vif.cb_mst.hsize     <= 0;
    vif.cb_mst.htrans    <= 0;
    vif.cb_mst.hwdata    <= 0;
    vif.cb_mst.hwrite    <= 0;
  endtask

  virtual protected task reset_listener();
    `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
    fork
      forever begin
        @(negedge vif.hresetn); // ASYNC reset  
        
        _do_drive_idle();
      end
    join_none
  endtask

endclass


`endif // LVC_AHB_MASTER_DRIVER_SV
driver的整体代码

【详细解读】

  virtual task drive_transfer(REQ t);
    // TODO implementation in child class
    case(t.burst_type)
      SINGLE: begin do_atomic_trans(t); end
      INCR  : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      WRAP4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      INCR4 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      WRAP8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      INCR8 : begin `uvm_error("TYPEERR", "burst type not supported yet") end
      WRAP16: begin `uvm_error("TYPEERR", "burst type not supported yet") end
      INCR16: begin `uvm_error("TYPEERR", "burst type not supported yet") end
      default: begin `uvm_error("TYPEERR", "burst type not defined") end
    endcase
  endtask

  virtual task do_atomic_trans(REQ t);
    case(t.xact_type)
      READ      : do_read(t);
      WRITE     : do_write(t);
      IDLE_XACT : begin `uvm_error("TYPEERR", "trans type not supported yet") end
      default: begin `uvm_error("TYPEERR", "trans type not defined") end
    endcase
  endtask
//在drvier_transfer函数中实现burst传输。
//burst的理解:如果你想要连续发送数据,那么你就会用到burst的这几个比特位。 
//你的master能发送和slave能接受两种同时满足情况的时候。

//首先利用case语句确定burst传输的类型,在03版本中的设计仅有SIGNLE的传输类型。
//接着利用调用函数的case语句确定hwrite的传输状态(xact_type),在当前设计中仅有读或写的状态。
  virtual task do_init_write(REQ t);
    wait_for_bus_grant();
    @(vif.cb_mst);  
    vif.cb_mst.htrans <= NSEQ;
    vif.cb_mst.haddr  <= t.addr;
    vif.cb_mst.hburst <= t.burst_type;
    vif.cb_mst.hsize  <= t.burst_size;
    vif.cb_mst.hwrite <= 1'b1;
    @(vif.cb_mst);   
    vif.cb_mst.hwdata <= t.data[0];
    forever begin
      @(negedge vif.hclk);
      if(vif.hready === 1'b1) begin
        break;
      end
      else
        @(vif.cb_mst);    
        //这行作用未知,注释掉无影响。
        //我认为可以注释,因为这个forever是为了等待hready信号,不满足直接继续循环时钟下降沿即可
        //else条件的cb_mst触发可有可无
    end
    // update current trans status
    t.trans_type = NSEQ;
    t.current_data_beat_num = 0; // start beat from 0 to make consistence with data array index
    t.all_beat_response[t.current_data_beat_num] = response_type_enum'(vif.hresp); 
  endtask

这是按照AHB协议实现的写操作。

如在T4时刻的上升沿到T5时刻的上升沿是地址周期,地址周期内给地址和初始化

在T5时刻的上升沿到T6时刻的上升沿是数据周期,数据周期内给数据,但是由HREADY信号决定需要几个cycle,图中的HREADY在此时拉低,因此需要等待HREADY拉高。

注意:按照协议的波形图,对于写操作而言,在数据周期已经将数据完成写操作;对于读操作而言,需要等待HREADY拉高后完成读操作

 

问:为什么@(negedge vif.hclk); 一定是在第二拍的下降沿去采样来获取hready的值?

答:因为我要得到的是hready的值为1才能发送数据,若不为0,则看下一拍的hready。

    事实上,在T5当前的时钟沿,HREADY采样到的值只能是时钟沿左边的值1。从设计的逻辑来讲,hready信号的反馈和当前这一拍的时钟沿,它是在同一拍里边发生的,在同一拍发生的情况,要按照组合逻辑来处理,组合逻辑来处理的话,它是没有是时钟这样一个延时的关系的。(组合逻辑是瞬发的)而现在需要捕捉时钟上升沿右边的这部分值,才能实现:判断在这一时钟沿是hready信号的为高的功能。

    如果你按照时钟上升沿捕捉的是hready这个值,那么它在T5时刻是1,T6时刻是0,T5-T6周期内可以发送数据。由于T6时刻是0,T7时刻是1,那么就是T6-T7周期内是hready为低,未就绪的。这样不仅会造成时序错误(不满足协议),还会使得验证代码延时时间冗余。

    一般使用统一延时时钟下降沿。统一延时时钟下降沿其实就是为了捕捉的更准确,方便复用,因为一旦使用时钟的下降沿则不需要关心时钟的周期。当然直接延时1ps、1ns也是可以的,但是这样要考虑时钟周期,若原时钟周期1ns,直接延时2ns,则会出现延时了两个时钟周期,造成错误。

 

    ......
//task do_init_read
t.data = new[t.current_data_beat_num+1](t.data);
    
t.data[0] = vif.hrdata; 
......

如何理解这两行代码?

data是一个存放从bus总线上读数据或者写数据的动态数组

第一行先理解一个简单的例子,如 a=new[3] (b) ,a和b都是数组。 这行代码表示创建一个新的动态数组 a,其长度为 3,并将数组 b 中的元素复制到新数组 a 中。 假设 b 是一个长度为 N 的数组,则代码 a=new[3](b) 将会执行以下操作:

  1. 创建一个长度为 3 的新数组 a
  2. 将数组 b 的前 3 个元素复制到新数组 a 中。如果 b 的长度小于 3,则只会复制全部可用的元素;如果 b 的长度大于 3,则只会复制前 3 个元素,数组 a 中其余位置的值将设置为默认值。
  3. 将新数组 a 的地址赋值给变量 a。 例如,如果 b 数组为 {1, 2, 3, 4, 5},则执行 a=new[3](b) 后,a 数组将包含 {1, 2, 3} 三个元素,其它两个元素的值将设置为默认值,而 b 数组的内容不变。需要注意的是,新数组 a 和原数组 b 是独立的数组,它们占用不同的内存空间,对它们的修改互不影响。

第一行的含义,创建一个数组长度为t.current_data_beat_num +1 的数组,然后将原数组t.data或截取或复制到新数组t.data中。具体例子如当多次运行时,current_data_beat_num会增加,然后将增加后的数组中填充原来数组后形成新的数组。 而第二行,含义是将读数据总线中的数据放置到req类型的t的data动态数组的第0位。因此第一二行之间没有太多关系。

 


5.4 lvc_monitor
一般用interface中的cb_monitor去采样数据,但是一旦用了cb_monitor采样数据,基本就不能脱离,不能一会用一会不用这样。
采样逻辑(协议)在driver中,在lvc_monitor中也要实现类似driver的采样逻辑

 

 

 

5.5 rkv_ahbram_haddr_word_unaligned_virt_seq

`ifndef RKV_AHBRAM_HADDR_WORD_UNALIGNED_VIRT_SEQ_SV
`define RKV_AHBRAM_HADDR_WORD_UNALIGNED_VIRT_SEQ_SV

//这里只有写和读,比较不在这个seq里面做,真正的比较交给scoreboard去做

class rkv_ahbram_haddr_word_unaligned_virt_seq extends rkv_ahbram_base_virtual_sequence;
  `uvm_object_utils(rkv_ahbram_haddr_word_unaligned_virt_seq)

  function new (string name = "rkv_ahbram_haddr_word_unaligned_virt_seq");
    super.new(name);
  endfunction

  virtual task body();
    bit [31:0] addr, data;
    burst_size_enum bsize;
    super.body();
    `uvm_info("body", "Entered...", UVM_LOW)
    for(int i=0; i<100; i++) begin
      std::randomize(bsize) with {bsize inside {BURST_SIZE_8BIT, BURST_SIZE_16BIT, BURST_SIZE_32BIT};};
      std::randomize(addr) with {addr inside {['h1000:'h1FFF]};
                                 bsize == BURST_SIZE_16BIT -> addr[0] == 0;
                                 bsize == BURST_SIZE_32BIT -> addr[1:0] == 0;  //符合协议 3-25 页,第三段话
                                };
      std::randomize(wr_val) with {wr_val == (i << 24) + (i << 16) + (i << 8) + i;};//每个byte位都有数,方便做debug
      data = wr_val;
      `uvm_do_with(single_write, {addr == local::addr; data == local::data; bsize == local::bsize;})
      `uvm_do_with(single_read, {addr == local::addr; bsize == local::bsize;})
    end
    `uvm_info("body", "Exiting...", UVM_LOW)
  endtask

endclass


`endif

 

 

5.6 rkv_ahbram_scoreboard

 

 

class rkv_ahbram_scoreboard extends rkv_ahbram_subscriber;
...

function void store_data_with_hsize(lvc_ahb_transaction tr, int beat);
    case(tr.burst_size)
//这么写的好处在于:低两位不care了,然后再给它补两位0
//这样不就可以实现字对齐的方式实现data了
      BURST_SIZE_8BIT   : mem[{tr.addr[31:2],2'b00}] = extract_current_beat_mem_data(tr, beat);
...

function bit check_data_with_hsize(lvc_ahb_transaction tr, int beat);
    bit[31:0] tdata = extract_valid_data(tr.data[beat], tr.addr, tr.burst_size); 
//tdata是当前读的数据 tdata--active data
    bit[31:0] mdata = extract_valid_data(mem[{tr.addr[31:2],2'b00}],  tr.addr, tr.burst_size);
//mdata是写的数据,存储在那里 wdata--memory data
    check_data_with_hsize = tdata == mdata ? 1 : 0;
    cfg.scb_check_count++;
    if(check_data_with_hsize)
      `uvm_info("DATACHK", $sformatf("ahbram[%0x] data expected 'h%0x = actual 'h%0x", tr.addr, mdata, tdata), UVM_HIGH)
    else begin
      cfg.scb_check_error++;
      `uvm_error("DATACHK", $sformatf("ahbram[%0x] data expected 'h%0x != actual 'h%0x", tr.addr, mdata, tdata))
    end
  endfunction

//注意:monitor 既监测写,也监测读
//"写"的数据是seq中randomize的数据,不需要&掉x值
//"读"的数据需要&这个操作


//extract_current_beat_mem_data:提取当前节拍内存数据
//处理发送的数据,mdata返回
  function bit [31:0] extract_current_beat_mem_data(lvc_ahb_transaction tr, int beat);
    bit [31:0] mdata = mem[{tr.addr[31:2],2'b00}];
    bit [31:0] tdata = tr.data[beat];
    case(tr.burst_size)
      BURST_SIZE_8BIT   : mdata[(tr.addr[1:0]*8 + 7) -:  8] = tdata >> (8*tr.addr[1:0]);//难点 
      //按照片选,片选过来以后把数据抄过来要它的最低位
      BURST_SIZE_16BIT  : mdata[(tr.addr[1]*16 + 15) -: 16] = tdata >> (16*tr.addr[1]);
      BURST_SIZE_32BIT  : mdata = tdata;
      BURST_SIZE_64BIT  : begin `uvm_error("TYPEERR", "burst size not supported") end
      default : begin `uvm_error("TYPEERR", "burst size not supported") end
    endcase
    return mdata;
  endfunction
...


//能寻找真正位移有效的data
  function bit [31:0] extract_valid_data([`LVC_AHB_MAX_DATA_WIDTH - 1:0] data
                                        ,[`LVC_AHB_MAX_ADDR_WIDTH - 1 : 0] addr
                                        ,burst_size_enum bsize);
  //为什么这里要做&? 因为我的data可能是一个高64位的,向右移8位,还有剩下的56位,但是我只取移位以后的低8位,所以要把剩下的&掉,&成0.
    case(bsize)
      BURST_SIZE_8BIT   : return (data >> (8*addr[1:0])) & 8'hFF;    
      BURST_SIZE_16BIT  : return (data >> (16*addr[1]) ) & 16'hFFFF;
      BURST_SIZE_32BIT  : return  data & 32'hFFFF_FFFF;
      BURST_SIZE_64BIT  : begin `uvm_error("TYPEERR", "burst size not supported") end
      default : begin `uvm_error("TYPEERR", "burst size not supported") end
    endcase
  endfunction
...

 

 

 

 

 

 

 

 

标签:begin,end,框架,burst,data,uvm,AHBRAM,type
From: https://www.cnblogs.com/yphasaki/p/17438252.html

相关文章

  • AHBRAM项目理解
    1.采样协议的理解  灰色的部分可以当做延迟或者亚稳态理解协议中的波形图中有的长有的短,这个暂态可能是由于组合电路的延迟,也有可能是时序电路的延迟。发送激励之前要满足协议,所以要实现AHB协议,lvc_ahb_types和lvc_ahb_transaction在master_driver中实现AHB协议 AHB协......
  • Mybatis框架
    1、什么是框架?它是我们软件开发中的一套解决方案,不同的框架解决不同的问题。使用框架的好处:框架封装了很多细节,使开发者可以使用极简的方式实现功能,大大提高开发效率。2、三层架构:表现层:用于展示数据业务层:处理业务需求持久层:与数据库交互的3、持久层......
  • 【2023 · CANN训练营第一季】——在华为AI加速型ECS上安装Pytorch和Tensorflow框架
    前言:在CANN训练营提供的华为云镜像环境,通过miniconda安装pytorch和Tensorflow框架。在模型迁移前准备阶段,可以用来在CPU上对模型训练进行验证。本文描述了安装过程,更换国内conda源、并分别下载例程,在Pytorch和Tensorflow框架下进行了CPU训练。还介绍了在Pytorch、Tensorflow虚拟环......
  • 用Java语言springboot框架开发工艺管理系统
    技术架构技术框架:SpringBoot2.0.0+Mybatis1.3.2+Shiro+jpa+lombok+Vue2+Mysql5.7+redis+nodejs16运行环境:jdk8+IntelliJIDEA+maven+宝塔面板宝塔部署教程回到IDEA,点击编辑器右侧maven图标,切换至prod,执行package,完成后就会在根目录里生成一个target目录,......
  • 基于springboot技术框架实现小区物业管理功能
    技术架构技术框架:springboot+mybatis+thymeleaf+Mysql5.7运行环境:jdk8+IntelliJIDEA+maven+宝塔面板宝塔部署教程回到IDEA,点击编辑器右侧maven图标,执行package,完成后就会在根目录里生成一个target目录,在里面会打包出一个jar文件。宝塔新建一个数据库,导入数据库文......
  • 用Java语言和Springboot框架实现宿舍管理系统
    技术架构技术框架:SpringBoot+SpringMVC+MyBatis+Layui+Mysql5.7+Axios+Echarts+POI运行环境:jdk8+IntelliJIDEA+maven+宝塔面板宝塔部署教程回到IDEA,点击编辑器右侧maven图标,执行package,完成后就会在根目录里生成一个target目录,在里面会打包出一个jar文件......
  • [golang]gin框架接收websocket通信
    前言WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket让客户端和服务端之间的数据交换变得非常简单,且允许服务器主动向客户端推送数据,并且之后客户端和服务端所有的通信都依靠这个专用协议进行。本文使用gin框架编写服务端应用,配置路由接收websocket请求并处理。......
  • ArchUnit 架构测试框架
    很多时候我们觉得架构比较复杂,或者架构比较玄幻,同时对于自己设计的架构方案心里总是没有底ArchUnit是一个不错的选择可以像单元测试一样,测试我们的架构是否合理(包含了包以及类的依赖,分层,循环依赖。。。。)说明对于希望检查自己方案架构问题的,可以尝试下ArchUnit工具参考资料h......
  • liteflow 一个强大的java 规则框架
    liteflow是一个java规则引擎,融合了编排以及规则引擎的能力,功能上很强大,支持比较复杂的编排(swtich,when,if,while,for。。。)同时还支持的不少的脚本引擎,我们同时还可以将规则存储在外部,实现一些规则管理以及reload说明liteflow对于java生态集成的特别好,是一个很不错的开源规则引......
  • java开发学习框架
    Java基础1.1.Java简介与安装1.2.Java基本语法1.3.数据类型与变量1.4.运算符与表达式1.5.流程控制(分支与循环)1.6.数组面向对象编程2.1.类与对象2.2.继承与多态2.3.接口与抽象类2.4.封装与访问控制2.5.重载与覆盖2.6.Java内存管理与垃圾回收Java常用......