计算机组成原理课设指南-基于FPGA的具有MIPS风格指令集的CPU设计

本文主要是介绍计算机组成原理课设指南-基于FPGA的具有MIPS风格指令集的CPU设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 前言
  • 这个课设到底要做什么
  • 开始设计
    • 准备
    • 分析实例
      • 指令的执行节拍
    • 作出修改
    • 添加外设接口
  • 结束
  • 附指导书样例源码

前言

笔者刚做完这个课程设计,希望把经验整理成这篇文章供学弟学妹们参考。

(2022.4.24更新:笔者学完了计算机体系结构,再回来看这篇文章发现真是漏洞白出,因此添加和修改了很多内容)

刚开始做这个课设的时候,笔者也是一头雾水的状态,计组这个学期学完考完之后就忘了不少,而EDA课设的题目实在太简单,目的只是让我们学会verilog语言和熟悉vivado的使用。所以计组课设一上来就要求我们设计与实现CPU而且几乎没给教程,时间也很紧张,只有一周多点的时间,所以笔者认为这是很不合理的。如果全靠自己摸索就很难有思路,也容易走弯路。

这篇文章是写给和我之前一样完全没接触过硬件这方面的小白的,所以很多地方的语言表述与硬件设计并不标准,如有错误欢迎指出。

这个课设到底要做什么

首先就是这个课设的目标,我们到底要做什么?

我们首先要明确,这个课设不同于EDA课设的完全使用硬件实现一个小功能,而是要用指令(软件)组成一段程序来实现功能,硬件部分只是用于组成一个以CPU为核心的SOC(片上系统)。我们需要设计实现一个能执行一些指令的CPU,然后以这个CPU为核心组建一个SOC,然后让它执行一系列指令。而指令其实就是一串只有0和1的机器码,由编译器把汇编语句编译得到(在这个课设中我们可以使用MARS这个软件快速地将汇编程序转换成一行行机器语言)。我们最终需要实现的程序就需要用汇编语句实现。

开始设计

准备

不管题目是怎么样的,这个课设都需要我们做一个简单的CPU出来,这是整个课设的重点。如果这个CPU已经能够正常执行一些指令,那么其实这个课设已经接近完成了,剩下的就是用这些指令设计汇编程序,然后把指令交给CPU去执行就行了。当然对于不同的题目还需要考虑不同的外设模块,但CPU部分都是大同小异的,因为这个阶段还不用考虑《计算机体系结构》里面的那些复杂设计。

我们需要的知识有:计算机组成原理、verilog、汇编语言(这里是MIPS指令集)以及vivado与FPGA的基本使用方法。

分析实例

实验指导书中给了我们一个现成的MIPS系统,我们如果能够彻底研究明白它的运行机制就可以很轻松地在它上面加以修改,以实现我们的需求。(这本指导书值得仔细阅读,对于课设相当重要)

首先,这个CPU最主要的特点就是:使用了多周期的流水线设计

这里简单解释一下,首先我们的指令并不是在一个时钟周期里就能完成它的操作的,经典的五段流水线中,所有指令的执行都需要5个周期,分别是:取指(把指令从指令存储器中取出,放到指令寄存器等待执行)、译码(分析OP字段的含义,从而发出不同的控制信号进行不同的操作)、执行访存写回(将结果放回寄存器)。

那么我们可以选择这样一种方案:一条一条地执行指令,在前一条指令的5个周期都结束后再开始下一条指令的取指、译码、执行、访存、写回。这就是无流水线设计。这种设计显然效率比较低,因为例如当前一条指令执行到译码阶段时,CPU的取指模块就处于空闲状态了,那此时完全可以流入下一条指令进行取指,并且也不会影响上一条指令的正常运行。这种设计就是流水线了。

如果仔细思考这种流水线设计,我们就会发现,这种设计很容易出现一个问题,那就是后一条指令在译码段读取寄存器数据时,有可能前一条指令还未将新数据写回,导致读的还是旧数据。这个问题在计算机体系结构中是需要重点考虑的问题,但是奇怪的是我在整个课设的过程中从来没有发现这个问题,但是它居然也不影响执行结果。当我时隔接近一年之后回过头再来看这个课设,具体细节早就忘记了,也许是某些节拍上的小设计解决了这个问题,但是我已经记不清了,这个问题就只好交给大家去思考了(才不是因为懒)。

然后是其他的一些很重要的特点:样例MIPS系统特点
对照书上源码(文章最后也附上了源码)可以看到,这个CPU的数据线只有8位宽,而指令有32位,所以取指需要取4次,而如果数据线能拓宽至32位则只需取1次。此外,这个课设的要求也是需要32位的,所以我们晚点需要对其进行修改。

样例中已经实现了9条指令,使用这些指令已经可以实现相当多的程序了。注意,其中的add,sub,and,or,slt属于R型指令。

源码中几乎没有注释,在这里说明一下各部件的功能:

  • top
    最顶层,连接CPU与存储器
  • mips.v
    CPU顶层,与存储器memory相连,从memory里读取指令/数据交给CPU处理
  • controller
    控制单元,输入op字段,输出各控制信号
  • alucontrol
    处理R型指令的funct字段,通过其确定alucont信号
  • datapath
    数据通路,用于实例化并连接各器件以传递数据
  • zerodetect
    判断输入的数据是否为0
  • flop
    D触发器
  • flopen
    带使能的D触发器
  • flopenr
    带复位与使能的D触发器
  • mux4
    四选一选择器
  • mux2
    二选一选择器
  • regfile
    通用寄存器

下面它的结构图,我们可以对照着代码,尝试将一条指令代入其中观察执行过程,初步理解CPU是如何处理指令的。
在这里插入图片描述
从PC开始,先从存储器Memory里读取PC对应的指令,送至指令寄存器instr[31:0],然后取其高6位(op字段)送入控制单元Controller进行译码,也就是将op转化位各个器件的控制信号,比如alusrca信号就是一个二路选择器mux2的控制信号,若alusrca=0则将PC放入ALU的输入端a,若alusrca=1则是把通用寄存器读出的数据1放入ALU的a端。

值得一提的是,这里通用寄存器可以同时读2个数或者写入1个数,ra1 ra2表示读的寄存器地址,输出数据为rd1 rd2,wa表示写的寄存器地址,wd是写入的数据。

指令的执行节拍

通过初步地阅读代码,我们能大概了解一条指令中数据是如何传递与操作的,那么为了更精确的确定执行动作的时间与指令的执行时长,并且方便更多指令的设计与实现,我们需要一个控制信号状态转化图。
在这里插入图片描述
这个图可能乍一看很复杂,但其实相当简单。它的每个状态都对应一个节拍下各个控制信号的状态,0123表示4个取值动作,也就是说取值需要4个节拍,在3号状态结束后才算取指完成.4号状态就是开始译码,值得注意的是,0123号状态的alusrca=0,alusrcb=01,aluop=00,也就是说,每个节拍下都会让PC+1,因为PC是按字节寻址,而一条指令是4个字节,所以4个节拍下来刚好PC+4(这个数还在aluresult里面),对应下一条指令。

接下来是4号DECODE节拍,这个节拍中把aluresult的值传给PC(就是PC+4)而ALU中a端传入的还是PC,b端传入的是BEQ指令对应的offset字段(跳转指令条数),aluop=00,也就是把PC与offset左移2位(即乘4,因为按字节寻址)相加,这个数就是跳转目标指令。这个节拍中开始把指令的各个字段送到不同的单元。

接下来的一个节拍就要根据不同的指令发出不同的控制信号。比如,BEQ就是把两个寄存器的数相减得到一个结果是否为0的zero信号,然后决定是否需要跳转,若需要跳转,则pcen=1,将PC=aluresult,此刻的PC就是跳转的目标,而若不跳转,则pcen=0,不另外赋值,PC仍是PC+4。

接下来的一个节拍,对于BEQ或者J指令,就直接开始下一条指令的取指,对于R型指令,就还需要一个回写阶段,把计算的结果存回寄存器。

作出修改

首先我们需要把8位数据线改为32位,除了把所有的WIDTH从8改为32以外,还要考虑到,此时的取指只需要1个节拍,所以需要对取指节拍进行相应修改,较为简单,此处不再赘述。同时,这里我们的PC可以不再采用按字节寻址,而是改成按字寻址,这样PC只需要+1,存储器里读指令也不需要再除以4(右移2位)了。但是,由于修改了PC寻址方式,所以跳转指令也需要作小小的修改,比如J的后26位不再需要左移2位(乘4)了,这里就不详细说明了。

添加外设接口

笔者一开始设计时由于不了解外设应当如何连接,所以直接将其连接在通用寄存器上了,也就导致与其相连的寄存器被限制只能存放这个外设端口的相关数据,这种设计是不对的。我们可以在存储器中将一些地址专门分配给外设使用,将需要显示的信息先保存到存储器中,再通过外设接口输出。

(其实这个部分在计组的IO那章应该有讲到,但是笔者没认真学 ==)

结束

至此,所有的硬件部分都已基本完成。接下来的任务就是编写汇编程序,转成机器码之后放入存储器让CPU执行。推荐使用Mars4.5编写汇编,这是一个很小的.jar但是功能齐全。

至于源码,我并不建议参考我的项目,我认为把样例代码进行修改要比直接照搬现成源码好得多,一是你会更熟悉CPU的结构,对能力的提升很有帮助,二是现成的源码可能有很多细节处理不标准不到位,比如我写SRA右移指令时,原本应当是由末16位确定右移位数,但是我考虑到我们只需要用到右移一位,所以我没有把末16位也参与运算而是直接把目标数>>1,属于是偷懒了;而且我们的外设部分并不好,上文也已经提到了,如果要改进还不如重构来得方便。

附指导书样例源码

笔者建议使用vscode或者其他文本编辑器阅读与编写代码,因为方便找变量。vivado中用vscode打开.v文件教程:
https://copyfuture.com/blogs-details/20200821104053326mo45esfedgvkgr6
同时,笔者也建议每个module各自放在一个.v文件里。

top.v:

module top #(parameter WIDTH = 8, REGBITS = 3)();reg clk;reg reset;wire memread, memwrite;wire [WIDTH-1:0] adr, writedata;wire [WIDTH-1:0] memdata;// instantiate devices to be testedmips #(WIDTH, REGBITS) dut(clk, reset, memdata, memread, memwrite, adr, writedata);// external memory for code and dataexmemory #(WIDTH) exmem(clk, memwrite, adr, writedata, memdata);// initialize testinitial beginreset <= 1; #22; reset <= 0;end// generate clock to sequence testsalways beginclk <= 1; #5; clk <= 0; #5;endalways @(negedge clk) beginif(memwrite)if (adr == 5 & writedata == 7)$display("Simulation completely successful");else $display("Simulation failed");end
endmodule

memory.v

// external memory accessed by MIPS
module exmemory #(parameter WIDTH = 8)(clk, memwrite, adr, writedata, 
memdata);input clk;input memwrite;input [WIDTH-1:0] adr, writedata;output reg [WIDTH-1:0] memdata;reg [31:0] RAM[(1<<WIDTH-2)1 : 0];wire [31:0] word;initial begin//这里使用了.dat文件初始化RAM,也可以直接RAM[]=进行初始化$readmemh("memfile.dat", RAM);end// read and write bytes from 32-bit wordalways @(posedge clk)if (memwrite) case (adr[1:0])2'b00: RAM[adr>>2][7:0] <= writedata;2'b01: RAM[adr>>2][15:8] <= writedata;2'b10: RAM[adr>>2][23:16] <= writedata;2'b11: RAM[adr>>2][31:24] <= writedata;endcaseassign word = RAM[adr>>2];always @(*)case (adr[1:0])2'b00: memdata <= word[31:24];2'b01: memdata <= word[23:16];2'b10: memdata <= word[15:8];2'b11: memdata <= word[7:0];endcase
endmodule

mips.v

// simplified MIPS processor
module mips #(parameter WIDTH = 8, REGBITS = 3)(input clk, reset, input [WIDTH-1:0] memdata, output memread, memwrite, output [WIDTH-1:0] adr, writedata);wire [31:0] instr;wire zero, alusrca, memtoreg, iord, pcen, regwrite, regdst;wire [1:0] aluop,pcsource,alusrcb;wire [3:0] irwrite;wire [2:0] alucont;controller cont(clk, reset, instr[31:26], zero, memread, memwrite, alusrca, memtoreg, iord, pcen, regwrite, regdst,pcsource, alusrcb, aluop, irwrite);alucontrol ac(aluop, instr[5:0], alucont);datapath #(WIDTH, REGBITS) dp(clk, reset, memdata, alusrca, memtoreg, iord, pcen, regwrite, regdst, pcsource, alusrcb, irwrite, alucont, zero, instr, adr, writedata);
endmodule

controller.v

module controller(input clk, reset, input [5:0] op, input zero, output reg memread, memwrite, alusrca, memtoreg, iord,output pcen, output reg regwrite, regdst, output reg [1:0] pcsource, alusrcb, aluop, output reg [3:0] irwrite);reg [3:0] state, nextstate;reg pcwrite, pcwritecond;parameter FETCH1 = 4'b0001;parameter FETCH2 = 4'b0010;parameter FETCH3 = 4'b0011;parameter FETCH4 = 4'b0100;parameter DECODE = 4'b0101;parameter MEMADR = 4'b0110;parameter LBRD = 4'b0111;parameter LBWR = 4'b1000;parameter SBWR = 4'b1001;parameter RTYPEEX = 4'b1010;parameter RTYPEWR = 4'b1011;parameter BEQEX = 4'b1100;parameter JEX = 4'b1101;parameter LB = 6'b100000;parameter SB = 6'b101000;parameter RTYPE = 6'b0;parameter BEQ = 6'b000100;parameter J = 6'b000010;// state registeralways @(posedge clk)if(reset) state <= FETCH1;else state <= nextstate;// next state logicalways @(*) begincase (state)FETCH1: nextstate <= FETCH2;FETCH2: nextstate <= FETCH3;FETCH3: nextstate <= FETCH4;FETCH4: nextstate <= DECODE;DECODE: case (op)LB: nextstate <= MEMADR;SB: nextstate <= MEMADR;RTYPE: nextstate <= RTYPEEX;BEQ: nextstate <= BEQEX;J: nextstate <= JEX;default: nextstate <= FETCH1;//default should never happenendcaseMEMADR: case (op)LB: nextstate <= LBRD;SB: nextstate <= SBWR;default: nextstate <= FETCH1;//default should never happenendcaseLBRD: nextstate <= LBWR;LBWR: nextstate <= FETCH1;SBWR: nextstate <= FETCH1;RTYPEEX: nextstate <= RTYPEWR;RTYPEWR: nextstate <= FETCH1;BEQEX: nextstate <= FETCH1;JEX: nextstate <= FETCH1;default: nextstate <= FETCH1; // default should never happenendcaseendalways @(*) begin// set all outputs to zero, then conditionally assert just the // appropriate onesirwrite <= 4'b0000;pcwrite <= 0; pcwritecond <= 0;regwrite <= 0; regdst <= 0;memread <= 0; memwrite <= 0;alusrca <= 0; alusrcb <= 2'b00;aluop <= 2'b00; pcsource <= 2'b00;iord <= 0; memtoreg <= 0;case (state)FETCH1: beginmemread <= 1; irwrite <= 4'b1000; alusrcb <= 2'b01; pcwrite <= 1;endFETCH2: beginmemread <= 1;irwrite <= 4'b0100;alusrcb <= 2'b01;pcwrite <= 1;endFETCH3: beginmemread <= 1;irwrite <= 4'b0010;alusrcb <= 2'b01;pcwrite <= 1;endFETCH4: beginmemread <= 1;irwrite <= 4'b0001;alusrcb <= 2'b01;pcwrite <= 1;endDECODE: alusrcb <= 2'b11;MEMADR: beginalusrca <= 1;alusrcb <= 2'b10;endLBRD: beginmemread <= 1;iord <= 1;endLBWR: beginregwrite <= 1;memtoreg <= 1;endSBWR: beginmemwrite <= 1;iord <= 1;endRTYPEEX: beginalusrca <= 1;aluop <= 2'b10;endRTYPEWR: beginregdst <= 1;regwrite <= 1;endBEQEX: beginalusrca <= 1;aluop <= 2'b01;pcwritecond <= 1;pcsource <= 2'b01;endJEX: beginpcwrite <= 1;pcsource <= 2'b10;endendcaseendassign pcen = pcwrite | (pcwritecond & zero); // PC enable
endmodule

alucontrol.v

module alucontrol(input [1:0] aluop, input [5:0] funct, output reg [2:0] alucont);always @(*)case (aluop)2'b00: alucont <= 3'b010; // add for lb/sb/addi2'b01: alucont <= 3'b110; // sub (for beq)default: case (funct) // R-Type instructions6'b100000: alucont <= 3'b010; // add6'b100010: alucont <= 3'b110; // subtract6'b100100: alucont <= 3'b000; // AND6'b100101: alucont <= 3'b001; // OR6'b101010: alucont <= 3'b111; // sltdefault: alucont <= 3'b101;// default should never happenendcaseendcase
endmodule

datapath.v

module datapath #(parameter WIDTH = 8, REGBITS = 3)(input clk, reset,input [WIDTH1:0] memdata,input alusrca, memtoreg, iord, pcen, input regwrite, regdst,input [1:0] pcsource, alusrcb,input [3:0] irwrite,input [2:0] alucont,output zero,output [31:0] instr,output [WIDTH1:0] adr, writedata);// the size of the parameters must be changed to match the WIDTH // parameterparameter CONST_ZERO = 8'b0;parameter CONST_ONE = 8'b1;wire [REGBITS1:0] ra1, ra2, wa;wire [WIDTH1:0] pc, nextpc, md, rd1, rd2, wd, a, src1, src2, aluresult, aluout, constx4;// shift left constant field by 2assign constx4 = {instr[WIDTH3:0], 2'b00};// register file address fieldsassign ra1 = instr[REGBITS+20 : 21];assign ra2 = instr[REGBITS+15 : 16];mux2 #(REGBITS) regmux(instr[REGBITS+15 : 16],instr[REGBITS+10 : 11], regdst, wa);// independent of bit width, load instruction into four 8-bit registers // over four cyclesflopen #(8) ir0(clk, irwrite[0], memdata[7:0], instr[7:0]);flopen #(8) ir1(clk, irwrite[1], memdata[7:0], instr[15:8]);flopen #(8) ir2(clk, irwrite[2], memdata[7:0], instr[23:16]);flopen #(8) ir3(clk, irwrite[3], memdata[7:0], instr[31:24]);// datapathflopenr #(WIDTH) pcreg(clk, reset, pcen, nextpc, pc);flop #(WIDTH) mdr(clk, memdata, md);flop #(WIDTH) areg(clk, rd1, a);flop #(WIDTH) wrd(clk, rd2, writedata);flop #(WIDTH) res(clk, aluresult, aluout);mux2 #(WIDTH) adrmux(pc, aluout, iord, adr);mux2 #(WIDTH) src1mux(pc, a, alusrca, src1);mux4 #(WIDTH) src2mux(writedata, CONST_ONE, instr[WIDTH-1:0], constx4, alusrcb, src2);mux4 #(WIDTH) pcmux(aluresult, aluout, constx4, CONST_ZERO, pcsource, nextpc);mux2 #(WIDTH) wdmux(aluout, md, memtoreg, wd);regfile #(WIDTH, REGBITS) rf(clk, regwrite, ra1, ra2, wa, wd, rd1, rd2);alu #(WIDTH) alunit(src1, src2, alucont, aluresult);zerodetect #(WIDTH) zd(aluresult, zero);
endmodule

alu.v

module alu #(parameter WIDTH = 8) (input [WIDTH1:0] a, b, input [2:0] alucont, output reg [WIDTH1:0] result);wire [WIDTH1:0] b2, sum, slt;assign b2 = alucont[2] ? ~b : b;assign sum = a + b2 + alucont[2];// slt should be 1 if most significant bit of sum is 1assign slt = sum[WIDTH - 1];always@(*)case (alucont[1:0])2'b00: result <= a & b;2'b01: result <= a | b;2'b10: result <= sum;2'b11: result <= slt;endcase
endmodule

regfile.v

module regfile #(parameter WIDTH = 8, REGBITS = 3)(input clk, input regwrite, input [REGBITS1:0] ra1, ra2, wa, input [WIDTH1:0] wd, output [WIDTH1:0] rd1, rd2);reg [WIDTH1:0] RAM[(1<<REGBITS)1 : 0];// 3-ported register file// read two ports combinationally// write third port on rising edge of clock// register 0 hardwired to 0always @(posedge clk)if (regwrite) RAM[wa] <= wd;assign rd1 = ra1 ? RAM[ra1] : 0;assign rd2 = ra2 ? RAM[ra2] : 0;
endmodule

zerodetect.v

module zerodetect #(parameter WIDTH = 8)(input [WIDTH1:0] a, output y);assign y = (a == 0);
endmodule

flop.v

module flop #(parameter WIDTH = 8)(input clk, input [WIDTH1:0] d, output reg [WIDTH1:0] q);always @(posedge clk)q <= d;
endmodule

flopen.v

module flopen #(parameter WIDTH = 8)(input clk, en,input [WIDTH1:0] d, output reg [WIDTH1:0] q);always @(posedge clk)if (en) q <= d;
endmodule

flopenr.v

module flopenr #(parameter WIDTH = 8)(input clk, reset, en,input [WIDTH1:0] d, output reg [WIDTH1:0] q);always @(posedge clk)if (reset) q <= 0;else if (en) q <= d;
endmodule

mux2.v

module mux2 #(parameter WIDTH = 8) (input [WIDTH1:0] d0, d1, input s, output [WIDTH1:0] y);assign y = s ? d1 : d0; 
endmodule

mux4.v

module mux4 #(parameter WIDTH = 8)(input [WIDTH1:0] d0, d1, d2, d3,input [1:0] s, output reg [WIDTH1:0] y);always @(*)case(s)2'b00: y <= d0;2'b01: y <= d1;2'b10: y <= d2;2'b11: y <= d3;endcase
endmodule

这篇关于计算机组成原理课设指南-基于FPGA的具有MIPS风格指令集的CPU设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/854508

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu